This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git
The following commit(s) were added to refs/heads/main by this push:
new 6cb53a51d refactor(cpp): remove abseil dependency (#3634)
6cb53a51d is described below
commit 6cb53a51d231779ae1697961ef4e422d17792ad0
Author: Shawn Yang <[email protected]>
AuthorDate: Wed Apr 29 16:21:52 2026 +0800
refactor(cpp): remove abseil dependency (#3634)
## Why?
## What does this PR do?
## Related issues
## AI Contribution Checklist
- [ ] Substantial AI assistance was used in this PR: `yes` / `no`
- [ ] If `yes`, I included a completed [AI Contribution
Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
in this PR description and the required `AI Usage Disclosure`.
- [ ] If `yes`, my PR description includes the required `ai_review`
summary and screenshot evidence of the final clean AI review results
from both fresh reviewers on the current PR diff or current HEAD after
the latest code changes.
## Does this PR introduce any user-facing change?
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
---
.github/workflows/ci.yml | 131 +++-
BUILD | 2 +-
LICENSE | 8 +-
MODULE.bazel | 5 +-
benchmarks/cpp/CMakeLists.txt | 5 +-
cpp/CMakeLists.txt | 34 +-
cpp/cmake/ForyConfig.cmake.in | 3 -
cpp/fory/serialization/BUILD | 2 +-
cpp/fory/serialization/CMakeLists.txt | 1 -
cpp/fory/serialization/context.cc | 6 +-
cpp/fory/serialization/context.h | 5 +-
cpp/fory/serialization/ref_resolver.h | 10 +-
cpp/fory/serialization/type_info.h | 4 +-
cpp/fory/serialization/type_resolver.cc | 42 +-
cpp/fory/serialization/type_resolver.h | 23 +-
cpp/fory/thirdparty/BUILD | 17 +
cpp/fory/thirdparty/CMakeLists.txt | 6 +
cpp/fory/thirdparty/flat_hash_map.h | 853 +++++++++++++++++++++
cpp/fory/thirdparty/flat_hash_map_test.cc | 125 +++
cpp/fory/util/BUILD | 5 -
cpp/fory/util/CMakeLists.txt | 12 -
cpp/fory/util/logging.cc | 42 +-
cpp/fory/util/logging_test.cc | 13 +-
LICENSE => licenses/LICENSE-abseil-cpp.txt | 107 +--
licenses/LICENSE-cython-abseil-example.txt | 21 -
python/pyfory/context.pxi | 35 +-
python/pyfory/includes/libabsl.pxd | 75 --
.../pyfory/includes/libflat_hash_map.pxd | 41 +-
python/pyfory/serialization.pyx | 42 +-
29 files changed, 1295 insertions(+), 380 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 36459f081..adfd5b080 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -47,8 +47,15 @@ jobs:
runs-on: ubuntu-latest
outputs:
cpp: ${{ steps.filter.outputs.cpp }}
+ cpp_code: ${{ steps.filter.outputs.cpp_code }}
+ java_code: ${{ steps.filter.outputs.java_code }}
python: ${{ steps.filter.outputs.python }}
graalvm: ${{ steps.filter.outputs.graalvm }}
+ rust: ${{ steps.filter.outputs.rust }}
+ swift: ${{ steps.filter.outputs.swift }}
+ javascript: ${{ steps.filter.outputs.javascript }}
+ kotlin: ${{ steps.filter.outputs.kotlin }}
+ scala: ${{ steps.filter.outputs.scala }}
steps:
- uses: actions/checkout@v5
- name: Detect changed paths
@@ -59,8 +66,15 @@ jobs:
run: |
if [[ "$GITHUB_EVENT_NAME" != "pull_request" ]]; then
echo "cpp=true" >> "$GITHUB_OUTPUT"
+ echo "cpp_code=true" >> "$GITHUB_OUTPUT"
+ echo "java_code=true" >> "$GITHUB_OUTPUT"
echo "python=true" >> "$GITHUB_OUTPUT"
echo "graalvm=true" >> "$GITHUB_OUTPUT"
+ echo "rust=true" >> "$GITHUB_OUTPUT"
+ echo "swift=true" >> "$GITHUB_OUTPUT"
+ echo "javascript=true" >> "$GITHUB_OUTPUT"
+ echo "kotlin=true" >> "$GITHUB_OUTPUT"
+ echo "scala=true" >> "$GITHUB_OUTPUT"
exit 0
fi
@@ -73,7 +87,19 @@ jobs:
echo "cpp=false" >> "$GITHUB_OUTPUT"
fi
- if grep -Eq
'^(cpp/|python/|bazel/|BUILD$|WORKSPACE$|MODULE\.bazel$|\.bazelrc$)' <<<
"$changed_files"; then
+ if grep -Eq '^(cpp/)' <<< "$changed_files"; then
+ echo "cpp_code=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "cpp_code=false" >> "$GITHUB_OUTPUT"
+ fi
+
+ if grep -Eq '^(java/)' <<< "$changed_files"; then
+ echo "java_code=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "java_code=false" >> "$GITHUB_OUTPUT"
+ fi
+
+ if grep -Eq '^(python/)' <<< "$changed_files"; then
echo "python=true" >> "$GITHUB_OUTPUT"
else
echo "python=false" >> "$GITHUB_OUTPUT"
@@ -85,6 +111,36 @@ jobs:
echo "graalvm=false" >> "$GITHUB_OUTPUT"
fi
+ if grep -Eq '^(rust/)' <<< "$changed_files"; then
+ echo "rust=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "rust=false" >> "$GITHUB_OUTPUT"
+ fi
+
+ if grep -Eq '^(swift/)' <<< "$changed_files"; then
+ echo "swift=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "swift=false" >> "$GITHUB_OUTPUT"
+ fi
+
+ if grep -Eq '^(javascript/)' <<< "$changed_files"; then
+ echo "javascript=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "javascript=false" >> "$GITHUB_OUTPUT"
+ fi
+
+ if grep -Eq '^(java/|kotlin/)' <<< "$changed_files"; then
+ echo "kotlin=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "kotlin=false" >> "$GITHUB_OUTPUT"
+ fi
+
+ if grep -Eq '^(java/|scala/)' <<< "$changed_files"; then
+ echo "scala=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "scala=false" >> "$GITHUB_OUTPUT"
+ fi
+
compiler:
name: Compiler CI
runs-on: ubuntu-latest
@@ -104,12 +160,13 @@ jobs:
java:
name: Java CI
+ needs: changes
runs-on: ubuntu-latest
env:
MY_VAR: "PATH"
strategy:
matrix:
- java-version: ["8", "11", "17", "21", "25"]
+ java-version: ${{ fromJSON(needs.changes.outputs.java_code == 'true'
&& '["8","11","17","21","25"]' || '["8"]') }}
steps:
- uses: actions/checkout@v5
- name: Set up JDK ${{ matrix.java-version }}
@@ -142,6 +199,8 @@ jobs:
openj9:
name: Openj9 Java CI
+ needs: changes
+ if: needs.changes.outputs.java_code == 'true'
runs-on: ubuntu-latest
env:
MY_VAR: "PATH"
@@ -173,6 +232,8 @@ jobs:
java21_windows:
name: Windows Java 21 CI
+ needs: changes
+ if: needs.changes.outputs.java_code == 'true'
runs-on: windows-2022
env:
MY_VAR: "PATH"
@@ -281,6 +342,8 @@ jobs:
swift:
name: Swift CI
+ needs: changes
+ if: needs.changes.outputs.swift == 'true'
runs-on: macos-latest
steps:
- uses: actions/checkout@v5
@@ -301,10 +364,6 @@ jobs:
runs-on: macos-latest
steps:
- uses: actions/checkout@v5
- - name: Set up Python 3.11
- uses: actions/setup-python@v5
- with:
- python-version: 3.11
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
@@ -340,6 +399,28 @@ jobs:
mvn -T16 --no-transfer-progress clean install -DskipTests
-Dmaven.javadoc.skip=true -Dmaven.source.skip=true
cd fory-core
mvn -T16 --no-transfer-progress test
-Dtest=org.apache.fory.xlang.SwiftXlangTest
+
+ swift_idl:
+ name: Swift IDL Tests
+ runs-on: macos-latest
+ steps:
+ - uses: actions/checkout@v5
+ - name: Set up Python 3.11
+ uses: actions/setup-python@v5
+ with:
+ python-version: 3.11
+ - name: Set up JDK 21
+ uses: actions/setup-java@v4
+ with:
+ java-version: 21
+ distribution: "temurin"
+ - name: Cache Maven local repository
+ uses: actions/cache@v4
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-maven-
- name: Generate Swift IDL test code
run: python ./integration_tests/idl_tests/generate_idl.py --lang swift
- name: Cache Swift IDL package build artifacts
@@ -361,6 +442,10 @@ jobs:
run: |
cd integration_tests/idl_tests/swift/idl_package
swift test --disable-automatic-resolution --skip-build
+ - name: Install Java artifacts for IDL tests
+ run: |
+ cd java
+ mvn -T16 --no-transfer-progress clean install -DskipTests
-Dmaven.javadoc.skip=true -Dmaven.source.skip=true
- name: Run Java IDL tests against Swift peer
env:
IDL_PEER_LANG: "swift"
@@ -399,6 +484,8 @@ jobs:
kotlin:
name: Kotlin CI
+ needs: changes
+ if: needs.changes.outputs.kotlin == 'true'
runs-on: ubuntu-latest
env:
MY_VAR: "PATH"
@@ -430,6 +517,8 @@ jobs:
scala:
name: Scala CI
+ needs: changes
+ if: needs.changes.outputs.scala == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
@@ -479,6 +568,8 @@ jobs:
javascript:
name: JavaScript CI
+ needs: changes
+ if: needs.changes.outputs.javascript == 'true'
strategy:
matrix:
node-version: [18, 24]
@@ -553,9 +644,11 @@ jobs:
rust:
name: Rust CI
+ needs: changes
strategy:
matrix:
- os: [ubuntu-latest, macos-14, macos-latest] # macos-14: arm64
+ # macos-14 is the arm64 runner; keep it for Rust code changes only.
+ os: ${{ fromJSON(needs.changes.outputs.rust == 'true' &&
'["ubuntu-latest","macos-14","macos-latest"]' ||
'["ubuntu-latest","macos-latest"]') }}
runs-on: ${{ matrix.os }}
timeout-minutes: 45
steps:
@@ -724,6 +817,8 @@ jobs:
cpp_sanitizers:
name: C++ Sanitizer (${{ matrix.sanitizer }})
+ needs: changes
+ if: needs.changes.outputs.cpp_code == 'true'
runs-on: ubuntu-latest
strategy:
matrix:
@@ -903,6 +998,8 @@ jobs:
python:
name: Python CI
+ needs: changes
+ if: needs.changes.outputs.python == 'true'
runs-on: ${{ matrix.os }}
strategy:
matrix:
@@ -927,13 +1024,17 @@ jobs:
echo "Cleaning Bazel cache to ensure fresh build..."
bazel clean --expunge || true
- name: Run Python CI
- shell: bash
- run: python ./ci/run_ci.py python
- - name: Run Python CI (ENABLE_FORY_CYTHON_SERIALIZATION=1)
shell: bash
env:
ENABLE_FORY_CYTHON_SERIALIZATION: "1"
run: python ./ci/run_ci.py python
+ - name: Run Python tests (ENABLE_FORY_CYTHON_SERIALIZATION=0)
+ shell: bash
+ env:
+ ENABLE_FORY_CYTHON_SERIALIZATION: "0"
+ run: |
+ cd python
+ pytest -v -s --durations=60 pyfory/tests
python_windows:
name: Python CI (${{ matrix.python-version }}, windows-2022)
@@ -963,13 +1064,17 @@ jobs:
echo "Cleaning Bazel cache to ensure fresh build..."
bazel clean --expunge || true
- name: Run Python CI
- shell: bash
- run: python ./ci/run_ci.py python
- - name: Run Python CI (ENABLE_FORY_CYTHON_SERIALIZATION=1)
shell: bash
env:
ENABLE_FORY_CYTHON_SERIALIZATION: "1"
run: python ./ci/run_ci.py python
+ - name: Run Python tests (ENABLE_FORY_CYTHON_SERIALIZATION=0)
+ shell: bash
+ env:
+ ENABLE_FORY_CYTHON_SERIALIZATION: "0"
+ run: |
+ cd python
+ pytest -v -s --durations=60 pyfory/tests
python_xlang:
name: Python Xlang Test
diff --git a/BUILD b/BUILD
index 2485123fd..69350bd55 100644
--- a/BUILD
+++ b/BUILD
@@ -51,7 +51,7 @@ pyx_library(
"//cpp/fory/util:fory_util",
"//cpp/fory/type:fory_type",
"//python/pyfory/cpp:_pyfory",
- "@abseil-cpp//absl/container:flat_hash_map",
+ "//cpp/fory/thirdparty:flat_hash_map",
],
)
diff --git a/LICENSE b/LICENSE
index 62e28df76..ec5b5a270 100644
--- a/LICENSE
+++ b/LICENSE
@@ -247,6 +247,10 @@ The text of each license is the standard Apache 2.0
license.
Files:
bazel/cython_library.bzl
+* abseil-cpp (https://github.com/abseil/abseil-cpp)
+ Files:
+ cpp/fory/thirdparty/flat_hash_map.h
+
================================================================
BSD-3-Clause licenses
@@ -282,10 +286,6 @@ The text of each license is also included in
licenses/LICENSE-[project].txt
Files:
javascript/packages/fory/lib/murmurHash3.ts
-* cython-abseil-sample (https://github.com/messense/cython-abseil-sample)
- Files:
- python/pyfory/includes/libabsl.pxd
-
================================================================
Public Domain
diff --git a/MODULE.bazel b/MODULE.bazel
index 5a6712054..40e986476 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -17,7 +17,7 @@
module(
name = "fory",
- version = "0.14.0",
+ version = "0.18.0",
)
# Platforms (needed for platform-specific build configurations)
@@ -49,9 +49,6 @@ bazel_dep(name = "cython", version = "3.1.3")
# Google Test
bazel_dep(name = "googletest", version = "1.15.2")
-# Abseil C++
-bazel_dep(name = "abseil-cpp", version = "20240722.0")
-
# Hedron's Compile Commands Extractor for Bazel
bazel_dep(name = "hedron_compile_commands", dev_dependency = True)
git_override(
diff --git a/benchmarks/cpp/CMakeLists.txt b/benchmarks/cpp/CMakeLists.txt
index 972f4d8ac..b1659c08b 100644
--- a/benchmarks/cpp/CMakeLists.txt
+++ b/benchmarks/cpp/CMakeLists.txt
@@ -42,15 +42,14 @@ endif()
# =============================================================================
include(FetchContent)
-# Fory C++ library (local source) - this will also fetch Abseil
+# Fory C++ library (local source)
FetchContent_Declare(
fory
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../cpp"
)
FetchContent_MakeAvailable(fory)
-# Protobuf - fetch to ensure Abseil version compatibility
-# Using v25.5 which is compatible with Abseil 20240722.0
+# Protobuf benchmark dependency may use Abseil; Fory targets must not depend
on it.
set(protobuf_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(protobuf_BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(protobuf_ABSL_PROVIDER "package" CACHE STRING "" FORCE)
diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt
index afb70641e..98accdba8 100644
--- a/cpp/CMakeLists.txt
+++ b/cpp/CMakeLists.txt
@@ -67,18 +67,23 @@ endif()
# Set Fory root directory for include paths (important when used as
subdirectory)
set(FORY_CPP_ROOT ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH "Fory C++ root
directory")
-# Fetch Abseil (required for debugging support)
-message(STATUS "Fetching Abseil via FetchContent")
-include(FetchContent)
-set(ABSL_PROPAGATE_CXX_STD ON)
-set(ABSL_BUILD_TESTING OFF)
-set(ABSL_ENABLE_INSTALL ON)
-FetchContent_Declare(
- abseil-cpp
- GIT_REPOSITORY https://github.com/abseil/abseil-cpp.git
- GIT_TAG 20240722.0
-)
-FetchContent_MakeAvailable(abseil-cpp)
+# Tests are declared inside runtime subdirectories, so GTest's CMake helpers
must
+# be available before those subdirectories are configured.
+if(FORY_BUILD_TESTS)
+ enable_testing()
+ find_package(GTest QUIET)
+ if(NOT GTest_FOUND)
+ include(FetchContent)
+ set(INSTALL_GTEST OFF CACHE BOOL "" FORCE)
+ FetchContent_Declare(
+ googletest
+ GIT_REPOSITORY https://github.com/google/googletest.git
+ GIT_TAG v1.15.2
+ )
+ FetchContent_MakeAvailable(googletest)
+ endif()
+ include(GoogleTest)
+endif()
# Add subdirectories
add_subdirectory(fory/thirdparty)
@@ -105,10 +110,7 @@ target_link_libraries(fory_lib INTERFACE
fory_serialization fory_row_format fory
add_library(fory::fory ALIAS fory_lib)
# Tests
-if(FORY_BUILD_TESTS)
- enable_testing()
- find_package(GTest REQUIRED)
- include(GoogleTest)
+if(FORY_BUILD_TESTS AND EXISTS
"${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt")
add_subdirectory(tests)
endif()
diff --git a/cpp/cmake/ForyConfig.cmake.in b/cpp/cmake/ForyConfig.cmake.in
index f14844247..1ddd407a3 100644
--- a/cpp/cmake/ForyConfig.cmake.in
+++ b/cpp/cmake/ForyConfig.cmake.in
@@ -19,9 +19,6 @@
include(CMakeFindDependencyMacro)
-# Check for Abseil (optional)
-find_package(absl CONFIG QUIET)
-
include("${CMAKE_CURRENT_LIST_DIR}/ForyTargets.cmake")
# Provide convenient component targets
diff --git a/cpp/fory/serialization/BUILD b/cpp/fory/serialization/BUILD
index 77f15a5d1..2308a59bf 100644
--- a/cpp/fory/serialization/BUILD
+++ b/cpp/fory/serialization/BUILD
@@ -38,10 +38,10 @@ cc_library(
strip_include_prefix = "/cpp",
deps = [
"//cpp/fory/meta:fory_meta",
+ "//cpp/fory/thirdparty:flat_hash_map",
"//cpp/fory/thirdparty:libmmh3",
"//cpp/fory/type:fory_type",
"//cpp/fory/util:fory_util",
- "@abseil-cpp//absl/container:flat_hash_map",
],
visibility = ["//visibility:public"],
)
diff --git a/cpp/fory/serialization/CMakeLists.txt
b/cpp/fory/serialization/CMakeLists.txt
index 0582554c7..b65047b5c 100644
--- a/cpp/fory/serialization/CMakeLists.txt
+++ b/cpp/fory/serialization/CMakeLists.txt
@@ -64,7 +64,6 @@ target_link_libraries(fory_serialization
fory_type
fory_util
fory_thirdparty
- absl::flat_hash_map
)
set_target_properties(fory_serialization PROPERTIES
diff --git a/cpp/fory/serialization/context.cc
b/cpp/fory/serialization/context.cc
index 28fbc6b1d..7967a3335 100644
--- a/cpp/fory/serialization/context.cc
+++ b/cpp/fory/serialization/context.cc
@@ -520,10 +520,10 @@ Result<const TypeInfo *, Error>
ReadContext::read_type_meta() {
return cached;
}
- auto cache_it = parsed_type_infos_.find(meta_header);
- if (cache_it != parsed_type_infos_.end()) {
+ auto *cache_entry = parsed_type_infos_.find(meta_header);
+ if (cache_entry != nullptr) {
// Found in cache - reuse and skip the bytes
- const TypeInfo *cached = cache_it->second;
+ const TypeInfo *cached = cache_entry->second;
reading_type_infos_.push_back(cached);
has_last_meta_header_ = true;
last_meta_header_ = meta_header;
diff --git a/cpp/fory/serialization/context.h b/cpp/fory/serialization/context.h
index 9b8063055..52d740ccf 100644
--- a/cpp/fory/serialization/context.h
+++ b/cpp/fory/serialization/context.h
@@ -23,14 +23,13 @@
#include "fory/serialization/config.h"
#include "fory/serialization/ref_resolver.h"
#include "fory/serialization/type_info.h"
+#include "fory/thirdparty/flat_hash_map.h"
#include "fory/type/type.h"
#include "fory/util/buffer.h"
#include "fory/util/error.h"
#include "fory/util/flat_int_map.h"
#include "fory/util/result.h"
-#include "absl/container/flat_hash_map.h"
-
#include <cassert>
#include <typeindex>
@@ -674,7 +673,7 @@ private:
// Index-based access (pointers to owned_reading_type_infos_ or
type_resolver)
std::vector<const TypeInfo *> reading_type_infos_;
// Cache by meta_header (pointers to cached_type_infos_)
- absl::flat_hash_map<int64_t, const TypeInfo *> parsed_type_infos_;
+ fory::flat_hash_map<int64_t, const TypeInfo *> parsed_type_infos_;
// Fast path for repeated type meta headers.
int64_t last_meta_header_ = 0;
const TypeInfo *last_meta_type_info_ = nullptr;
diff --git a/cpp/fory/serialization/ref_resolver.h
b/cpp/fory/serialization/ref_resolver.h
index c22255283..01c2b8eec 100644
--- a/cpp/fory/serialization/ref_resolver.h
+++ b/cpp/fory/serialization/ref_resolver.h
@@ -22,7 +22,7 @@
#include "fory/util/error.h"
#include "fory/util/result.h"
-#include "absl/container/flat_hash_map.h"
+#include "fory/thirdparty/flat_hash_map.h"
#include <any>
#include <cstdint>
@@ -65,10 +65,10 @@ public:
}
auto address = reinterpret_cast<uintptr_t>(ptr.get());
- auto it = ptr_to_id_.find(address);
- if (it != ptr_to_id_.end()) {
+ auto *entry = ptr_to_id_.find(address);
+ if (entry != nullptr) {
writer.write_int8(static_cast<int8_t>(RefFlag::Ref));
- writer.write_var_uint32(it->second);
+ writer.write_var_uint32(entry->second);
return true;
}
@@ -91,7 +91,7 @@ public:
}
private:
- absl::flat_hash_map<uintptr_t, uint32_t> ptr_to_id_;
+ fory::flat_hash_map<uintptr_t, uint32_t> ptr_to_id_;
uint32_t next_id_;
};
diff --git a/cpp/fory/serialization/type_info.h
b/cpp/fory/serialization/type_info.h
index 1379727da..f31b14cc0 100644
--- a/cpp/fory/serialization/type_info.h
+++ b/cpp/fory/serialization/type_info.h
@@ -23,7 +23,7 @@
#include "fory/util/error.h"
#include "fory/util/result.h"
-#include "absl/container/flat_hash_map.h"
+#include "fory/thirdparty/flat_hash_map.h"
#include <any>
#include <cstdint>
@@ -125,7 +125,7 @@ struct TypeInfo {
bool is_external = false;
std::unique_ptr<TypeMeta> type_meta;
std::vector<size_t> sorted_indices;
- absl::flat_hash_map<std::string, size_t> name_to_index;
+ fory::flat_hash_map<std::string, size_t> name_to_index;
std::vector<uint8_t> type_def;
Harness harness;
// Pre-encoded meta strings for efficient writing (avoids re-encoding on each
diff --git a/cpp/fory/serialization/type_resolver.cc
b/cpp/fory/serialization/type_resolver.cc
index d5038ddd6..b17f959fb 100644
--- a/cpp/fory/serialization/type_resolver.cc
+++ b/cpp/fory/serialization/type_resolver.cc
@@ -1324,11 +1324,11 @@ encode_meta_string(const std::string &value, bool
is_namespace) {
Result<const TypeInfo *, Error>
TypeResolver::get_type_info(const std::type_index &type_index) const {
// For runtime polymorphic lookups (e.g., smart pointers with dynamic types)
- auto it = type_info_by_runtime_type_.find(type_index);
- if (it == type_info_by_runtime_type_.end()) {
+ auto *entry = type_info_by_runtime_type_.find(type_index);
+ if (entry == nullptr) {
return Unexpected(Error::type_error("TypeInfo not found for type_index"));
}
- return it->second;
+ return entry->second;
}
Result<std::unique_ptr<TypeResolver>, Error>
@@ -1343,7 +1343,7 @@ TypeResolver::build_final_type_resolver() {
final_resolver->finalized_ = true;
// Build mapping from old pointers to new pointers for rebuilding lookup maps
- absl::flat_hash_map<const TypeInfo *, TypeInfo *> ptr_map;
+ fory::flat_hash_map<const TypeInfo *, TypeInfo *> ptr_map;
// Deep clone all existing TypeInfo objects
for (const auto &info : type_infos_) {
@@ -1352,26 +1352,31 @@ TypeResolver::build_final_type_resolver() {
ptr_map[info.get()] = new_ptr;
final_resolver->type_infos_.push_back(std::move(cloned));
}
+ auto remap_type_info = [&ptr_map](const TypeInfo *old_ptr) {
+ auto *entry = ptr_map.find(old_ptr);
+ FORY_CHECK(entry != nullptr);
+ return entry->second;
+ };
// Rebuild lookup maps with new pointers
for (const auto &[key, old_ptr] : type_info_by_ctid_) {
- final_resolver->type_info_by_ctid_.put(key, ptr_map[old_ptr]);
+ final_resolver->type_info_by_ctid_.put(key, remap_type_info(old_ptr));
}
for (const auto &[key, old_ptr] : type_info_by_id_) {
- final_resolver->type_info_by_id_.put(key, ptr_map[old_ptr]);
+ final_resolver->type_info_by_id_.put(key, remap_type_info(old_ptr));
}
for (const auto &[key, old_ptr] : user_type_info_by_id_) {
- final_resolver->user_type_info_by_id_.put(key, ptr_map[old_ptr]);
+ final_resolver->user_type_info_by_id_.put(key, remap_type_info(old_ptr));
}
for (const auto &[key, old_ptr] : type_info_by_name_) {
- final_resolver->type_info_by_name_[key] = ptr_map[old_ptr];
+ final_resolver->type_info_by_name_[key] = remap_type_info(old_ptr);
}
for (const auto &[key, old_ptr] : type_info_by_runtime_type_) {
- final_resolver->type_info_by_runtime_type_[key] = ptr_map[old_ptr];
+ final_resolver->type_info_by_runtime_type_[key] = remap_type_info(old_ptr);
}
for (const auto &[key, old_ptr] : partial_type_infos_) {
- final_resolver->partial_type_infos_.put(key, ptr_map[old_ptr]);
+ final_resolver->partial_type_infos_.put(key, remap_type_info(old_ptr));
}
// Process all partial type infos to build complete type metadata
@@ -1418,7 +1423,7 @@ std::unique_ptr<TypeResolver> TypeResolver::clone() const
{
cloned->finalized_ = finalized_;
// Build mapping from old pointers to new pointers
- absl::flat_hash_map<const TypeInfo *, TypeInfo *> ptr_map;
+ fory::flat_hash_map<const TypeInfo *, TypeInfo *> ptr_map;
// Deep clone all TypeInfo objects
for (const auto &info : type_infos_) {
@@ -1427,22 +1432,27 @@ std::unique_ptr<TypeResolver> TypeResolver::clone()
const {
ptr_map[info.get()] = new_ptr;
cloned->type_infos_.push_back(std::move(cloned_info));
}
+ auto remap_type_info = [&ptr_map](const TypeInfo *old_ptr) {
+ auto *entry = ptr_map.find(old_ptr);
+ FORY_CHECK(entry != nullptr);
+ return entry->second;
+ };
// Rebuild lookup maps with new pointers
for (const auto &[key, old_ptr] : type_info_by_ctid_) {
- cloned->type_info_by_ctid_.put(key, ptr_map[old_ptr]);
+ cloned->type_info_by_ctid_.put(key, remap_type_info(old_ptr));
}
for (const auto &[key, old_ptr] : type_info_by_id_) {
- cloned->type_info_by_id_.put(key, ptr_map[old_ptr]);
+ cloned->type_info_by_id_.put(key, remap_type_info(old_ptr));
}
for (const auto &[key, old_ptr] : user_type_info_by_id_) {
- cloned->user_type_info_by_id_.put(key, ptr_map[old_ptr]);
+ cloned->user_type_info_by_id_.put(key, remap_type_info(old_ptr));
}
for (const auto &[key, old_ptr] : type_info_by_name_) {
- cloned->type_info_by_name_[key] = ptr_map[old_ptr];
+ cloned->type_info_by_name_[key] = remap_type_info(old_ptr);
}
for (const auto &[key, old_ptr] : type_info_by_runtime_type_) {
- cloned->type_info_by_runtime_type_[key] = ptr_map[old_ptr];
+ cloned->type_info_by_runtime_type_[key] = remap_type_info(old_ptr);
}
// Note: Don't copy partial_type_infos_ - clone should only be used on
// finalized resolvers
diff --git a/cpp/fory/serialization/type_resolver.h
b/cpp/fory/serialization/type_resolver.h
index d13157b5e..e5c746781 100644
--- a/cpp/fory/serialization/type_resolver.h
+++ b/cpp/fory/serialization/type_resolver.h
@@ -42,8 +42,6 @@
#include <utility>
#include <vector>
-#include "absl/container/flat_hash_map.h"
-
#include "fory/meta/field.h"
#include "fory/meta/field_info.h"
#include "fory/meta/type_traits.h"
@@ -51,6 +49,7 @@
#include "fory/serialization/serializer.h"
#include "fory/serialization/serializer_traits.h"
#include "fory/serialization/type_info.h"
+#include "fory/thirdparty/flat_hash_map.h"
#include "fory/type/type.h"
#include "fory/util/buffer.h"
#include "fory/util/error.h"
@@ -1194,11 +1193,11 @@ private:
util::U64PtrMap<TypeInfo> type_info_by_ctid_{256};
util::U32PtrMap<TypeInfo> type_info_by_id_{256};
util::U64PtrMap<TypeInfo> user_type_info_by_id_{256};
- absl::flat_hash_map<std::string, TypeInfo *> type_info_by_name_;
+ fory::flat_hash_map<std::string, TypeInfo *> type_info_by_name_;
util::U64PtrMap<TypeInfo> partial_type_infos_{256};
// For runtime polymorphic lookups (smart pointers) - uses std::type_index
- absl::flat_hash_map<std::type_index, TypeInfo *> type_info_by_runtime_type_;
+ fory::flat_hash_map<std::type_index, TypeInfo *> type_info_by_runtime_type_;
};
// Alias for backward compatibility (already defined above as top-level)
@@ -1579,11 +1578,11 @@ TypeResolver::build_struct_type_info(uint32_t type_id,
uint32_t user_type_id,
entry->sorted_indices.clear();
entry->sorted_indices.reserve(field_count);
for (const auto &sorted_field : sorted_fields) {
- auto it = entry->name_to_index.find(sorted_field.field_name);
- FORY_CHECK(it != entry->name_to_index.end())
+ auto *name_entry = entry->name_to_index.find(sorted_field.field_name);
+ FORY_CHECK(name_entry != nullptr)
<< "Sorted field name '" << sorted_field.field_name
<< "' not found in original struct definition";
- entry->sorted_indices.push_back(it->second);
+ entry->sorted_indices.push_back(name_entry->second);
}
entry->harness = make_struct_harness<T>();
@@ -1889,8 +1888,8 @@ TypeResolver::register_type_internal(uint64_t ctid,
if (raw_ptr->register_by_name) {
name_key = make_name_key(raw_ptr->namespace_name, raw_ptr->type_name);
- auto it = type_info_by_name_.find(name_key);
- if (it != type_info_by_name_.end()) {
+ auto *entry = type_info_by_name_.find(name_key);
+ if (entry != nullptr) {
return Unexpected(Error::invalid(
"Type already registered for namespace '" + raw_ptr->namespace_name +
"' and name '" + raw_ptr->type_name + "'"));
@@ -1949,9 +1948,9 @@ inline Result<const TypeInfo *, Error>
TypeResolver::get_type_info_by_name(const std::string &ns,
const std::string &type_name) const {
auto key = make_name_key(ns, type_name);
- auto it = type_info_by_name_.find(key);
- if (it != type_info_by_name_.end()) {
- return it->second;
+ auto *entry = type_info_by_name_.find(key);
+ if (entry != nullptr) {
+ return entry->second;
}
return Unexpected(Error::type_error("TypeInfo not found for type: " + ns +
"." + type_name));
diff --git a/cpp/fory/thirdparty/BUILD b/cpp/fory/thirdparty/BUILD
index f54e00356..e951e391a 100644
--- a/cpp/fory/thirdparty/BUILD
+++ b/cpp/fory/thirdparty/BUILD
@@ -9,3 +9,20 @@ cc_library(
linkstatic=True,
visibility = ["//visibility:public"],
)
+
+cc_library(
+ name = "flat_hash_map",
+ hdrs = ["flat_hash_map.h"],
+ strip_include_prefix = "/cpp",
+ visibility = ["//visibility:public"],
+)
+
+cc_test(
+ name = "flat_hash_map_test",
+ srcs = ["flat_hash_map_test.cc"],
+ deps = [
+ ":flat_hash_map",
+ "@googletest//:gtest",
+ "@googletest//:gtest_main",
+ ],
+)
diff --git a/cpp/fory/thirdparty/CMakeLists.txt
b/cpp/fory/thirdparty/CMakeLists.txt
index 048922fae..ec0800ef4 100644
--- a/cpp/fory/thirdparty/CMakeLists.txt
+++ b/cpp/fory/thirdparty/CMakeLists.txt
@@ -30,3 +30,9 @@ set_target_properties(fory_thirdparty PROPERTIES
POSITION_INDEPENDENT_CODE ON
OUTPUT_NAME fory_thirdparty
)
+
+if(FORY_BUILD_TESTS)
+ add_executable(fory_thirdparty_flat_hash_map_test flat_hash_map_test.cc)
+ target_link_libraries(fory_thirdparty_flat_hash_map_test fory_thirdparty
GTest::gtest GTest::gtest_main)
+ gtest_discover_tests(fory_thirdparty_flat_hash_map_test)
+endif()
diff --git a/cpp/fory/thirdparty/flat_hash_map.h
b/cpp/fory/thirdparty/flat_hash_map.h
new file mode 100644
index 000000000..ae5439487
--- /dev/null
+++ b/cpp/fory/thirdparty/flat_hash_map.h
@@ -0,0 +1,853 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// This file is a compact Fory-owned SwissTable flat hash map derived from
+// Abseil's flat_hash_map/raw_hash_set design. It keeps the
performance-critical
+// pieces Fory relies on: separate control bytes, H1/H2 hash splitting,
+// triangular group probing, and SIMD group matching on SSE2/AArch64 NEON.
+
+#pragma once
+
+#include <algorithm>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <functional>
+#include <initializer_list>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <new>
+#include <stdexcept>
+#include <string>
+#include <type_traits>
+#include <typeindex>
+#include <utility>
+#include <vector>
+
+#if defined(_MSC_VER)
+#define FORY_FLAT_HASH_MAP_ALWAYS_INLINE __forceinline
+#else
+#define FORY_FLAT_HASH_MAP_ALWAYS_INLINE inline __attribute__((always_inline))
+#endif
+
+#if defined(__SSE2__) || defined(_M_X64) ||
\
+ (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
+#include <emmintrin.h>
+#define FORY_FLAT_HASH_MAP_HAVE_SSE2 1
+#endif
+
+#if defined(_MSC_VER)
+#include <intrin.h>
+#endif
+
+#if defined(__ARM_NEON) || defined(__ARM_NEON__)
+#include <arm_neon.h>
+#define FORY_FLAT_HASH_MAP_HAVE_NEON 1
+#endif
+
+namespace fory {
+namespace detail {
+namespace flat_hash_map_internal {
+
+using ctrl_t = int8_t;
+
+constexpr ctrl_t kEmpty = static_cast<ctrl_t>(-128);
+constexpr ctrl_t kDeleted = static_cast<ctrl_t>(-2);
+constexpr float kMaxLoadFactor = 0.875f;
+
+FORY_FLAT_HASH_MAP_ALWAYS_INLINE size_t mix_hash(size_t hash) {
+ uint64_t x = static_cast<uint64_t>(hash);
+ x ^= x >> 32;
+ x *= 0xd6e8feb86659fd93ULL;
+ x ^= x >> 32;
+ if constexpr (sizeof(size_t) < sizeof(uint64_t)) {
+ return static_cast<size_t>(x ^ (x >> 32));
+ } else {
+ return static_cast<size_t>(x);
+ }
+}
+
+FORY_FLAT_HASH_MAP_ALWAYS_INLINE uint8_t h2(size_t hash) {
+ return static_cast<uint8_t>(hash >> (sizeof(size_t) * 8 - 7));
+}
+
+FORY_FLAT_HASH_MAP_ALWAYS_INLINE bool is_full(ctrl_t ctrl) { return ctrl >= 0;
}
+FORY_FLAT_HASH_MAP_ALWAYS_INLINE bool is_empty(ctrl_t ctrl) {
+ return ctrl == kEmpty;
+}
+FORY_FLAT_HASH_MAP_ALWAYS_INLINE bool is_deleted(ctrl_t ctrl) {
+ return ctrl == kDeleted;
+}
+
+inline size_t next_capacity(size_t requested) {
+ size_t capacity = 16;
+ while (capacity < requested) {
+ capacity <<= 1;
+ }
+ return capacity;
+}
+
+inline uint32_t trailing_zeros(uint32_t value) {
+#if defined(_MSC_VER)
+ unsigned long index = 0;
+ _BitScanForward(&index, value);
+ return static_cast<uint32_t>(index);
+#else
+ return static_cast<uint32_t>(__builtin_ctz(value));
+#endif
+}
+
+inline uint32_t trailing_zeros(uint64_t value) {
+#if defined(_MSC_VER) && defined(_M_X64)
+ unsigned long index = 0;
+ _BitScanForward64(&index, value);
+ return static_cast<uint32_t>(index);
+#elif defined(_MSC_VER)
+ uint32_t low = static_cast<uint32_t>(value);
+ if (low != 0) {
+ return trailing_zeros(low);
+ }
+ return 32 + trailing_zeros(static_cast<uint32_t>(value >> 32));
+#else
+ return static_cast<uint32_t>(__builtin_ctzll(value));
+#endif
+}
+
+inline uint32_t clear_lowest_bit(uint32_t mask) { return mask & (mask - 1); }
+
+inline uint64_t clear_lowest_bit(uint64_t mask) { return mask & (mask - 1); }
+
+template <typename T, typename = void> struct default_hash {
+ size_t operator()(const T &value) const { return std::hash<T>{}(value); }
+};
+
+template <typename A, typename B> struct default_hash<std::pair<A, B>> {
+ size_t operator()(const std::pair<A, B> &value) const {
+ size_t first = default_hash<A>{}(value.first);
+ size_t second = default_hash<B>{}(value.second);
+ return first ^
+ (second + 0x9e3779b97f4a7c15ULL + (first << 6) + (first >> 2));
+ }
+};
+
+template <size_t Width> class probe_seq {
+public:
+ probe_seq(size_t hash, size_t mask) : mask_(mask), offset_(hash & mask) {}
+
+ size_t offset() const { return offset_; }
+
+ void next() {
+ index_ += Width;
+ offset_ = (offset_ + index_) & mask_;
+ }
+
+ size_t index() const { return index_; }
+
+private:
+ size_t mask_;
+ size_t offset_;
+ size_t index_ = 0;
+};
+
+#if defined(FORY_FLAT_HASH_MAP_HAVE_SSE2)
+class group_sse2 {
+public:
+ static constexpr size_t kWidth = 16;
+ using mask_type = uint32_t;
+
+ explicit group_sse2(const ctrl_t *control)
+ : control_(_mm_loadu_si128(reinterpret_cast<const __m128i *>(control)))
{}
+
+ uint32_t match(uint8_t tag) const {
+ const __m128i match = _mm_set1_epi8(static_cast<char>(tag));
+ return static_cast<uint32_t>(
+ _mm_movemask_epi8(_mm_cmpeq_epi8(match, control_)));
+ }
+
+ uint32_t mask_empty() const {
+ const __m128i match = _mm_set1_epi8(static_cast<char>(kEmpty));
+ return static_cast<uint32_t>(
+ _mm_movemask_epi8(_mm_cmpeq_epi8(match, control_)));
+ }
+
+ uint32_t mask_empty_or_deleted() const {
+ const __m128i sentinel = _mm_set1_epi8(static_cast<char>(-1));
+ return static_cast<uint32_t>(
+ _mm_movemask_epi8(_mm_cmpgt_epi8(sentinel, control_)));
+ }
+
+ static uint32_t lowest_bit(mask_type mask) { return trailing_zeros(mask); }
+
+ static mask_type clear_lowest_bit(mask_type mask) {
+ return flat_hash_map_internal::clear_lowest_bit(mask);
+ }
+
+private:
+ __m128i control_;
+};
+#endif
+
+#if defined(FORY_FLAT_HASH_MAP_HAVE_NEON)
+constexpr uint64_t kMsbs8Bytes = 0x8080808080808080ULL;
+
+class group_neon {
+public:
+ static constexpr size_t kWidth = 8;
+ using mask_type = uint64_t;
+
+ explicit group_neon(const ctrl_t *control)
+ : control_(vld1_u8(reinterpret_cast<const uint8_t *>(control))) {}
+
+ mask_type match(uint8_t tag) const {
+ const uint8x8_t result = vceq_u8(control_, vdup_n_u8(tag));
+ uint64_t raw = vget_lane_u64(vreinterpret_u64_u8(result), 0);
+ return raw & kMsbs8Bytes;
+ }
+
+ mask_type mask_empty() const {
+ const uint8x8_t result =
+ vceq_s8(vreinterpret_s8_u8(control_), vdup_n_s8(kEmpty));
+ uint64_t raw = vget_lane_u64(vreinterpret_u64_u8(result), 0);
+ return raw & kMsbs8Bytes;
+ }
+
+ mask_type mask_empty_or_deleted() const {
+ const uint8x8_t result = vcgt_s8(vdup_n_s8(static_cast<int8_t>(-1)),
+ vreinterpret_s8_u8(control_));
+ uint64_t raw = vget_lane_u64(vreinterpret_u64_u8(result), 0);
+ return raw & kMsbs8Bytes;
+ }
+
+ static uint32_t lowest_bit(mask_type mask) {
+ return trailing_zeros(mask) >> 3;
+ }
+
+ static mask_type clear_lowest_bit(mask_type mask) {
+ return flat_hash_map_internal::clear_lowest_bit(mask);
+ }
+
+private:
+ uint8x8_t control_;
+};
+#endif
+
+class group_portable {
+public:
+ static constexpr size_t kWidth = 8;
+ using mask_type = uint32_t;
+
+ explicit group_portable(const ctrl_t *control) : control_(control) {}
+
+ uint32_t match(uint8_t tag) const {
+ uint32_t mask = 0;
+ for (size_t i = 0; i < kWidth; ++i) {
+ mask |= static_cast<uint32_t>(control_[i] == static_cast<ctrl_t>(tag))
+ << i;
+ }
+ return mask;
+ }
+
+ uint32_t mask_empty() const {
+ uint32_t mask = 0;
+ for (size_t i = 0; i < kWidth; ++i) {
+ mask |= static_cast<uint32_t>(is_empty(control_[i])) << i;
+ }
+ return mask;
+ }
+
+ uint32_t mask_empty_or_deleted() const {
+ uint32_t mask = 0;
+ for (size_t i = 0; i < kWidth; ++i) {
+ mask |= static_cast<uint32_t>(control_[i] < static_cast<ctrl_t>(-1)) <<
i;
+ }
+ return mask;
+ }
+
+ static uint32_t lowest_bit(mask_type mask) { return trailing_zeros(mask); }
+
+ static mask_type clear_lowest_bit(mask_type mask) {
+ return flat_hash_map_internal::clear_lowest_bit(mask);
+ }
+
+private:
+ const ctrl_t *control_;
+};
+
+#if defined(FORY_FLAT_HASH_MAP_HAVE_SSE2)
+using group = group_sse2;
+#elif defined(FORY_FLAT_HASH_MAP_HAVE_NEON)
+using group = group_neon;
+#else
+using group = group_portable;
+#endif
+
+} // namespace flat_hash_map_internal
+} // namespace detail
+
+template <typename K, typename V,
+ typename Hash = detail::flat_hash_map_internal::default_hash<K>,
+ typename Eq = std::equal_to<K>,
+ typename Alloc = std::allocator<std::pair<K, V>>>
+class flat_hash_map {
+public:
+ using key_type = K;
+ using mapped_type = V;
+ using value_type = std::pair<K, V>;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+ using hasher = Hash;
+ using key_equal = Eq;
+ using allocator_type = Alloc;
+ using reference = value_type &;
+ using const_reference = const value_type &;
+ using pointer = value_type *;
+ using const_pointer = const value_type *;
+
+private:
+ using ctrl_t = detail::flat_hash_map_internal::ctrl_t;
+ using group = detail::flat_hash_map_internal::group;
+
+ struct slot_type {
+ alignas(value_type) unsigned char storage[sizeof(value_type)];
+
+ value_type *value() { return reinterpret_cast<value_type *>(storage); }
+
+ const value_type *value() const {
+ return reinterpret_cast<const value_type *>(storage);
+ }
+ };
+
+public:
+ // Iteration is retained for copy/rebuild/diagnostic paths. It scans control
+ // bytes and is not the optimized serialization fast path; hot lookup/insert
+ // code should use find()'s nullable pointer result and insert/emplace.
+ template <bool IsConst> class iterator_base {
+ using map_pointer =
+ std::conditional_t<IsConst, const flat_hash_map *, flat_hash_map *>;
+
+ public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = flat_hash_map::value_type;
+ using difference_type = flat_hash_map::difference_type;
+ using reference =
+ std::conditional_t<IsConst, const value_type &, value_type &>;
+ using pointer =
+ std::conditional_t<IsConst, const value_type *, value_type *>;
+
+ iterator_base() : map_(nullptr), index_(0) {}
+
+ iterator_base(map_pointer map, size_t index) : map_(map), index_(index) {
+ skip_empty();
+ }
+
+ template <bool B = IsConst, typename = std::enable_if_t<B>>
+ iterator_base(const iterator_base<false> &other)
+ : map_(other.map_), index_(other.index_) {}
+
+ reference operator*() const { return *map_->slots_[index_].value(); }
+
+ pointer operator->() const { return map_->slots_[index_].value(); }
+
+ iterator_base &operator++() {
+ ++index_;
+ skip_empty();
+ return *this;
+ }
+
+ iterator_base operator++(int) {
+ iterator_base copy = *this;
+ ++(*this);
+ return copy;
+ }
+
+ iterator_base &operator--() = delete;
+
+ bool operator==(const iterator_base &other) const {
+ return map_ == other.map_ && index_ == other.index_;
+ }
+
+ bool operator!=(const iterator_base &other) const {
+ return !(*this == other);
+ }
+
+ private:
+ friend class flat_hash_map;
+ template <bool> friend class iterator_base;
+
+ void skip_empty() {
+ while (map_ != nullptr && index_ < map_->capacity_ &&
+ !detail::flat_hash_map_internal::is_full(map_->ctrl_[index_])) {
+ ++index_;
+ }
+ }
+
+ map_pointer map_;
+ size_t index_;
+ };
+
+ using iterator = iterator_base<false>;
+ using const_iterator = iterator_base<true>;
+
+ flat_hash_map() { initialize(0); }
+
+ explicit flat_hash_map(size_t bucket_count) { initialize(bucket_count); }
+
+ flat_hash_map(std::initializer_list<value_type> values) {
+ initialize(values.size());
+ for (const auto &value : values) {
+ emplace(value.first, value.second);
+ }
+ }
+
+ flat_hash_map(const flat_hash_map &other)
+ : hash_(other.hash_), eq_(other.eq_) {
+ initialize(other.size_);
+ for (const auto &value : other) {
+ emplace(value.first, value.second);
+ }
+ }
+
+ flat_hash_map(flat_hash_map &&other) noexcept
+ : ctrl_(std::move(other.ctrl_)), slots_(std::move(other.slots_)),
+ size_(other.size_), capacity_(other.capacity_),
+ growth_left_(other.growth_left_), hash_(std::move(other.hash_)),
+ eq_(std::move(other.eq_)) {
+ other.size_ = 0;
+ other.capacity_ = 0;
+ other.growth_left_ = 0;
+ }
+
+ flat_hash_map &operator=(const flat_hash_map &other) {
+ if (this == &other) {
+ return *this;
+ }
+ flat_hash_map copy(other);
+ swap(copy);
+ return *this;
+ }
+
+ flat_hash_map &operator=(flat_hash_map &&other) noexcept {
+ if (this == &other) {
+ return *this;
+ }
+ destroy_values();
+ ctrl_ = std::move(other.ctrl_);
+ slots_ = std::move(other.slots_);
+ size_ = other.size_;
+ capacity_ = other.capacity_;
+ growth_left_ = other.growth_left_;
+ hash_ = std::move(other.hash_);
+ eq_ = std::move(other.eq_);
+ other.size_ = 0;
+ other.capacity_ = 0;
+ other.growth_left_ = 0;
+ return *this;
+ }
+
+ ~flat_hash_map() { destroy_values(); }
+
+ iterator begin() { return iterator(this, 0); }
+
+ const_iterator begin() const { return const_iterator(this, 0); }
+
+ const_iterator cbegin() const { return begin(); }
+
+ iterator end() { return iterator(this, capacity_); }
+
+ const_iterator end() const { return const_iterator(this, capacity_); }
+
+ const_iterator cend() const { return end(); }
+
+ bool empty() const { return size_ == 0; }
+
+ size_t size() const { return size_; }
+
+ size_t bucket_count() const { return capacity_; }
+
+ size_t capacity() const { return capacity_; }
+
+ float max_load_factor() const {
+ return detail::flat_hash_map_internal::kMaxLoadFactor;
+ }
+
+ void max_load_factor(float) {}
+
+ void clear() {
+ destroy_values();
+ std::fill(ctrl_.begin(), ctrl_.end(),
+ detail::flat_hash_map_internal::kEmpty);
+ size_ = 0;
+ growth_left_ = max_load_for_capacity(capacity_);
+ }
+
+ void reserve(size_t count) {
+ size_t min_capacity = capacity_for_size(count);
+ if (min_capacity > capacity_) {
+ rehash(min_capacity);
+ }
+ }
+
+ void rehash(size_t count) {
+ size_t target = std::max(count, capacity_for_size(size_));
+ target = detail::flat_hash_map_internal::next_capacity(target);
+ if (target == capacity_) {
+ return;
+ }
+
+ flat_hash_map replacement(target);
+ replacement.hash_ = hash_;
+ replacement.eq_ = eq_;
+ for (size_t i = 0; i < capacity_; ++i) {
+ if (detail::flat_hash_map_internal::is_full(ctrl_[i])) {
+ replacement.insert_existing(std::move(*slots_[i].value()));
+ slots_[i].value()->~value_type();
+ set_ctrl(i, detail::flat_hash_map_internal::kEmpty);
+ }
+ }
+ size_ = 0;
+ swap(replacement);
+ }
+
+ // Hot-path lookup API: returns nullptr on miss and avoids iterator objects.
+ // Iterators remain available for maintenance paths that need range
traversal.
+ value_type *find(const K &key) {
+ size_t index = find_index(key);
+ return index == npos() ? nullptr : slots_[index].value();
+ }
+
+ // Hot-path lookup API: returns nullptr on miss and avoids iterator objects.
+ const value_type *find(const K &key) const {
+ size_t index = find_index(key);
+ return index == npos() ? nullptr : slots_[index].value();
+ }
+
+ size_t count(const K &key) const { return find_index(key) == npos() ? 0 : 1;
}
+
+ bool contains(const K &key) const { return count(key) != 0; }
+
+ V &at(const K &key) {
+ size_t index = find_index(key);
+ if (index == npos()) {
+ throw std::out_of_range("fory::flat_hash_map::at");
+ }
+ return slots_[index].value()->second;
+ }
+
+ const V &at(const K &key) const {
+ size_t index = find_index(key);
+ if (index == npos()) {
+ throw std::out_of_range("fory::flat_hash_map::at");
+ }
+ return slots_[index].value()->second;
+ }
+
+ V &operator[](const K &key) { return try_emplace_default(key); }
+
+ V &operator[](K &&key) { return try_emplace_default(std::move(key)); }
+
+ std::pair<value_type *, bool> insert(const value_type &value) {
+ return emplace(value.first, value.second);
+ }
+
+ std::pair<value_type *, bool> insert(value_type &&value) {
+ return emplace(std::move(value.first), std::move(value.second));
+ }
+
+ template <typename InputIt> void insert(InputIt first, InputIt last) {
+ for (; first != last; ++first) {
+ insert(*first);
+ }
+ }
+
+ template <typename... Args>
+ std::pair<value_type *, bool> emplace(Args &&...args) {
+ return emplace_value(value_type(std::forward<Args>(args)...));
+ }
+
+ template <
+ typename KeyArg, typename MappedArg,
+ typename = std::enable_if_t<std::is_same<std::decay_t<KeyArg>,
K>::value>>
+ std::pair<value_type *, bool> emplace(KeyArg &&key, MappedArg &&mapped) {
+ return emplace_key_value(std::forward<KeyArg>(key),
+ std::forward<MappedArg>(mapped));
+ }
+
+ void swap(flat_hash_map &other) noexcept {
+ ctrl_.swap(other.ctrl_);
+ slots_.swap(other.slots_);
+ std::swap(size_, other.size_);
+ std::swap(capacity_, other.capacity_);
+ std::swap(growth_left_, other.growth_left_);
+ std::swap(hash_, other.hash_);
+ std::swap(eq_, other.eq_);
+ }
+
+ size_t erase(const K &key) {
+ size_t index = find_index(key);
+ if (index == npos()) {
+ return 0;
+ }
+ erase_at(index);
+ return 1;
+ }
+
+private:
+ static constexpr size_t npos() { return std::numeric_limits<size_t>::max(); }
+
+ void initialize(size_t requested_capacity) {
+ capacity_ = detail::flat_hash_map_internal::next_capacity(
+ std::max<size_t>(requested_capacity, group::kWidth));
+ ctrl_.assign(capacity_ + group::kWidth,
+ detail::flat_hash_map_internal::kEmpty);
+ slots_.clear();
+ slots_.resize(capacity_);
+ size_ = 0;
+ growth_left_ = max_load_for_capacity(capacity_);
+ }
+
+ void destroy_values() {
+ for (size_t i = 0; i < capacity_; ++i) {
+ if (detail::flat_hash_map_internal::is_full(ctrl_[i])) {
+ slots_[i].value()->~value_type();
+ }
+ }
+ }
+
+ static size_t max_load_for_capacity(size_t capacity) {
+ return std::max<size_t>(
+ 1, static_cast<size_t>(capacity *
+
detail::flat_hash_map_internal::kMaxLoadFactor));
+ }
+
+ static size_t capacity_for_size(size_t size) {
+ size_t needed =
+ static_cast<size_t>(static_cast<double>(size) /
+ detail::flat_hash_map_internal::kMaxLoadFactor
+
+ 1);
+ return detail::flat_hash_map_internal::next_capacity(needed);
+ }
+
+ FORY_FLAT_HASH_MAP_ALWAYS_INLINE size_t hash_key(const K &key) const {
+ return detail::flat_hash_map_internal::mix_hash(hash_(key));
+ }
+
+ FORY_FLAT_HASH_MAP_ALWAYS_INLINE void set_ctrl(size_t index, ctrl_t value) {
+ ctrl_[index] = value;
+ if (index < group::kWidth) {
+ ctrl_[capacity_ + index] = value;
+ }
+ }
+
+ FORY_FLAT_HASH_MAP_ALWAYS_INLINE size_t slot_index(size_t group_offset,
+ size_t group_index) const
{
+ return (group_offset + group_index) & (capacity_ - 1);
+ }
+
+ FORY_FLAT_HASH_MAP_ALWAYS_INLINE size_t find_index(const K &key) const {
+ size_t hash = hash_key(key);
+ uint8_t tag = detail::flat_hash_map_internal::h2(hash);
+ detail::flat_hash_map_internal::probe_seq<group::kWidth> seq(hash,
+ capacity_ -
1);
+
+ while (seq.index() < capacity_) {
+ group current(ctrl_.data() + seq.offset());
+ typename group::mask_type candidates = current.match(tag);
+ while (candidates != 0) {
+ uint32_t group_index = group::lowest_bit(candidates);
+ size_t index = slot_index(seq.offset(), group_index);
+ if (eq_(slots_[index].value()->first, key)) {
+ return index;
+ }
+ candidates = group::clear_lowest_bit(candidates);
+ }
+ if (current.mask_empty() != 0) {
+ return npos();
+ }
+ seq.next();
+ }
+ return npos();
+ }
+
+ FORY_FLAT_HASH_MAP_ALWAYS_INLINE size_t find_insert_index(const K &key,
+ size_t hash,
+ bool &found) const
{
+ uint8_t tag = detail::flat_hash_map_internal::h2(hash);
+ detail::flat_hash_map_internal::probe_seq<group::kWidth> seq(hash,
+ capacity_ -
1);
+ size_t first_deleted = npos();
+
+ while (seq.index() < capacity_) {
+ group current(ctrl_.data() + seq.offset());
+ typename group::mask_type candidates = current.match(tag);
+ while (candidates != 0) {
+ uint32_t group_index = group::lowest_bit(candidates);
+ size_t index = slot_index(seq.offset(), group_index);
+ if (eq_(slots_[index].value()->first, key)) {
+ found = true;
+ return index;
+ }
+ candidates = group::clear_lowest_bit(candidates);
+ }
+
+ typename group::mask_type non_full = current.mask_empty_or_deleted();
+ while (non_full != 0) {
+ uint32_t group_index = group::lowest_bit(non_full);
+ size_t index = slot_index(seq.offset(), group_index);
+ if (detail::flat_hash_map_internal::is_deleted(ctrl_[index])) {
+ if (first_deleted == npos()) {
+ first_deleted = index;
+ }
+ } else if (detail::flat_hash_map_internal::is_empty(ctrl_[index])) {
+ found = false;
+ return first_deleted == npos() ? index : first_deleted;
+ }
+ non_full = group::clear_lowest_bit(non_full);
+ }
+ seq.next();
+ }
+
+ found = false;
+ return first_deleted;
+ }
+
+ std::pair<value_type *, bool> emplace_value(value_type value) {
+ if (growth_left_ == 0) {
+ rehash(capacity_ * 2);
+ }
+ size_t hash = hash_key(value.first);
+ bool found = false;
+ size_t index = find_insert_index(value.first, hash, found);
+ if (found) {
+ return {slots_[index].value(), false};
+ }
+ if (index == npos()) {
+ rehash(capacity_ * 2);
+ hash = hash_key(value.first);
+ index = find_insert_index(value.first, hash, found);
+ }
+ ctrl_t previous = ctrl_[index];
+ new (slots_[index].value()) value_type(std::move(value));
+ set_ctrl(index,
+ static_cast<ctrl_t>(detail::flat_hash_map_internal::h2(hash)));
+ ++size_;
+ if (!detail::flat_hash_map_internal::is_deleted(previous)) {
+ --growth_left_;
+ }
+ return {slots_[index].value(), true};
+ }
+
+ template <typename KeyArg> V &try_emplace_default(KeyArg &&key) {
+ if (growth_left_ == 0) {
+ rehash(capacity_ * 2);
+ }
+ size_t hash = hash_key(key);
+ bool found = false;
+ size_t index = find_insert_index(key, hash, found);
+ if (found) {
+ return slots_[index].value()->second;
+ }
+ if (index == npos()) {
+ rehash(capacity_ * 2);
+ hash = hash_key(key);
+ index = find_insert_index(key, hash, found);
+ if (found) {
+ return slots_[index].value()->second;
+ }
+ }
+ ctrl_t previous = ctrl_[index];
+ new (slots_[index].value()) value_type(std::forward<KeyArg>(key), V{});
+ set_ctrl(index,
+ static_cast<ctrl_t>(detail::flat_hash_map_internal::h2(hash)));
+ ++size_;
+ if (!detail::flat_hash_map_internal::is_deleted(previous)) {
+ --growth_left_;
+ }
+ return slots_[index].value()->second;
+ }
+
+ template <typename KeyArg, typename MappedArg>
+ std::pair<value_type *, bool> emplace_key_value(KeyArg &&key,
+ MappedArg &&mapped) {
+ if (growth_left_ == 0) {
+ rehash(capacity_ * 2);
+ }
+ size_t hash = hash_key(key);
+ bool found = false;
+ size_t index = find_insert_index(key, hash, found);
+ if (found) {
+ return {slots_[index].value(), false};
+ }
+ if (index == npos()) {
+ rehash(capacity_ * 2);
+ hash = hash_key(key);
+ index = find_insert_index(key, hash, found);
+ if (found) {
+ return {slots_[index].value(), false};
+ }
+ }
+ ctrl_t previous = ctrl_[index];
+ new (slots_[index].value())
+ value_type(std::forward<KeyArg>(key), std::forward<MappedArg>(mapped));
+ set_ctrl(index,
+ static_cast<ctrl_t>(detail::flat_hash_map_internal::h2(hash)));
+ ++size_;
+ if (!detail::flat_hash_map_internal::is_deleted(previous)) {
+ --growth_left_;
+ }
+ return {slots_[index].value(), true};
+ }
+
+ void insert_existing(value_type value) {
+ size_t hash = hash_key(value.first);
+ bool found = false;
+ size_t index = find_insert_index(value.first, hash, found);
+ new (slots_[index].value()) value_type(std::move(value));
+ set_ctrl(index,
+ static_cast<ctrl_t>(detail::flat_hash_map_internal::h2(hash)));
+ ++size_;
+ --growth_left_;
+ }
+
+ void erase_at(size_t index) {
+ slots_[index].value()->~value_type();
+ set_ctrl(index, detail::flat_hash_map_internal::kDeleted);
+ --size_;
+ }
+
+ std::vector<ctrl_t> ctrl_;
+ std::vector<slot_type> slots_;
+ size_t size_ = 0;
+ size_t capacity_ = 0;
+ size_t growth_left_ = 0;
+ Hash hash_;
+ Eq eq_;
+};
+
+template <typename K, typename V, typename Hash, typename Eq, typename Alloc>
+void swap(flat_hash_map<K, V, Hash, Eq, Alloc> &left,
+ flat_hash_map<K, V, Hash, Eq, Alloc> &right) noexcept {
+ left.swap(right);
+}
+
+} // namespace fory
+
+#undef FORY_FLAT_HASH_MAP_HAVE_SSE2
+#undef FORY_FLAT_HASH_MAP_HAVE_NEON
+#undef FORY_FLAT_HASH_MAP_ALWAYS_INLINE
diff --git a/cpp/fory/thirdparty/flat_hash_map_test.cc
b/cpp/fory/thirdparty/flat_hash_map_test.cc
new file mode 100644
index 000000000..7fecb5b05
--- /dev/null
+++ b/cpp/fory/thirdparty/flat_hash_map_test.cc
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "fory/thirdparty/flat_hash_map.h"
+
+#include <cstdint>
+#include <string>
+#include <typeindex>
+
+#include "gtest/gtest.h"
+
+namespace fory {
+
+TEST(FlatHashMapTest, RequiredApiCoverage) {
+ flat_hash_map<uint64_t, std::string> map;
+ EXPECT_TRUE(map.empty());
+
+ auto inserted = map.emplace(1, "one");
+ EXPECT_TRUE(inserted.second);
+ EXPECT_EQ(inserted.first->second, "one");
+ EXPECT_FALSE(map.emplace(1, "duplicate").second);
+ EXPECT_EQ(map[1], "one");
+
+ map[2] = "two";
+ EXPECT_EQ(map.size(), 2);
+ EXPECT_NE(map.find(1), nullptr);
+ EXPECT_EQ(map.find(42), nullptr);
+
+ size_t old_bucket_count = map.bucket_count();
+ map.reserve(256);
+ EXPECT_GE(map.bucket_count(), old_bucket_count);
+ map.rehash(512);
+ EXPECT_GE(map.bucket_count(), 512);
+
+ size_t visited = 0;
+ for (const auto &[key, value] : map) {
+ EXPECT_TRUE((key == 1 && value == "one") || (key == 2 && value == "two"));
+ ++visited;
+ }
+ EXPECT_EQ(visited, 2);
+
+ flat_hash_map<uint64_t, std::string> copied = map;
+ EXPECT_EQ(copied[1], "one");
+ EXPECT_EQ(copied[2], "two");
+
+ flat_hash_map<uint64_t, std::string> moved = std::move(copied);
+ EXPECT_EQ(moved[1], "one");
+ EXPECT_EQ(moved[2], "two");
+
+ moved.clear();
+ EXPECT_TRUE(moved.empty());
+ moved[3] = "three";
+ EXPECT_EQ(moved[3], "three");
+}
+
+TEST(FlatHashMapTest, SupportsRequiredKeyShapes) {
+ flat_hash_map<uint64_t, int> u64_map;
+ u64_map[123] = 1;
+ EXPECT_EQ(u64_map.find(123)->second, 1);
+
+ int target = 0;
+ flat_hash_map<uintptr_t, int> pointer_map;
+ pointer_map[reinterpret_cast<uintptr_t>(&target)] = 2;
+ EXPECT_EQ(pointer_map.find(reinterpret_cast<uintptr_t>(&target))->second, 2);
+
+ flat_hash_map<std::string, int> string_map;
+ string_map["abc"] = 3;
+ EXPECT_EQ(string_map.find("abc")->second, 3);
+
+ flat_hash_map<std::type_index, int> type_map;
+ type_map[std::type_index(typeid(int))] = 4;
+ EXPECT_EQ(type_map.find(std::type_index(typeid(int)))->second, 4);
+
+ flat_hash_map<std::pair<int64_t, int64_t>, int> pair_map;
+ pair_map[std::make_pair<int64_t, int64_t>(7, 9)] = 5;
+ EXPECT_EQ(pair_map.find(std::make_pair<int64_t, int64_t>(7, 9))->second, 5);
+}
+
+TEST(FlatHashMapTest, HandlesGroupProbingAndDeletedSlots) {
+ flat_hash_map<uint64_t, uint64_t> map;
+ for (uint64_t i = 0; i < 4096; ++i) {
+ map.emplace(i, i * 3);
+ }
+ EXPECT_EQ(map.size(), 4096);
+
+ for (uint64_t i = 0; i < 4096; i += 2) {
+ EXPECT_EQ(map.erase(i), 1);
+ }
+ EXPECT_EQ(map.size(), 2048);
+
+ for (uint64_t i = 1; i < 4096; i += 2) {
+ auto *entry = map.find(i);
+ ASSERT_NE(entry, nullptr);
+ EXPECT_EQ(entry->second, i * 3);
+ }
+
+ for (uint64_t i = 4096; i < 6144; ++i) {
+ map.emplace(i, i * 5);
+ }
+ EXPECT_EQ(map.size(), 4096);
+
+ for (uint64_t i = 4096; i < 6144; ++i) {
+ auto *entry = map.find(i);
+ ASSERT_NE(entry, nullptr);
+ EXPECT_EQ(entry->second, i * 5);
+ }
+}
+
+} // namespace fory
diff --git a/cpp/fory/util/BUILD b/cpp/fory/util/BUILD
index 881891378..ed38b341f 100644
--- a/cpp/fory/util/BUILD
+++ b/cpp/fory/util/BUILD
@@ -7,11 +7,6 @@ cc_library(
strip_include_prefix = "/cpp",
alwayslink=True,
linkstatic=True,
- deps = [
- "@abseil-cpp//absl/debugging:failure_signal_handler",
- "@abseil-cpp//absl/debugging:stacktrace",
- "@abseil-cpp//absl/debugging:symbolize",
- ],
visibility = ["//visibility:public"],
)
diff --git a/cpp/fory/util/CMakeLists.txt b/cpp/fory/util/CMakeLists.txt
index e6f4e52f1..a543154cd 100644
--- a/cpp/fory/util/CMakeLists.txt
+++ b/cpp/fory/util/CMakeLists.txt
@@ -50,14 +50,6 @@ target_include_directories(fory_util
$<INSTALL_INTERFACE:include>
)
-# Link Abseil for debugging support
-target_link_libraries(fory_util
- PUBLIC
- absl::failure_signal_handler
- absl::stacktrace
- absl::symbolize
-)
-
set_target_properties(fory_util PROPERTIES
POSITION_INDEPENDENT_CODE ON
OUTPUT_NAME fory_util
@@ -93,10 +85,6 @@ if(FORY_BUILD_TESTS)
target_link_libraries(fory_util_time_util_test fory_util GTest::gtest
GTest::gtest_main)
gtest_discover_tests(fory_util_time_util_test)
- add_executable(fory_util_int_map_test int_map_test.cc)
- target_link_libraries(fory_util_int_map_test fory_util GTest::gtest
GTest::gtest_main)
- gtest_discover_tests(fory_util_int_map_test)
-
add_executable(fory_util_flat_int_map_test flat_int_map_test.cc)
target_link_libraries(fory_util_flat_int_map_test fory_util GTest::gtest
GTest::gtest_main)
gtest_discover_tests(fory_util_flat_int_map_test)
diff --git a/cpp/fory/util/logging.cc b/cpp/fory/util/logging.cc
index 983e26166..24fe7e006 100644
--- a/cpp/fory/util/logging.cc
+++ b/cpp/fory/util/logging.cc
@@ -18,12 +18,18 @@
*/
#include "fory/util/logging.h"
-#include "absl/debugging/failure_signal_handler.h"
-#include "absl/debugging/stacktrace.h"
-#include "absl/debugging/symbolize.h"
#include "fory/util/time_util.h"
+
+#if !defined(_WIN32)
+#include <execinfo.h>
+#endif
+
+#include <algorithm>
+#include <array>
+#include <cctype>
+#include <cstdlib>
+#include <sstream>
#include <unordered_map>
-#include <vector>
namespace std {
template <> struct hash<fory::ForyLogLevel> {
@@ -36,18 +42,30 @@ namespace fory {
const ForyLogLevel fory_severity_threshold = ForyLog::get_log_level();
std::string get_call_trace() {
- std::vector<void *> local_stack;
- local_stack.resize(100);
- absl::GetStackTrace(local_stack.data(), 100, 0);
- static constexpr size_t buf_size = 16 * 1024;
- char buf[buf_size];
+#if !defined(_WIN32)
+ std::array<void *, 100> local_stack{};
+ int frames =
+ backtrace(local_stack.data(), static_cast<int>(local_stack.size()));
std::string output;
- for (auto &stack : local_stack) {
- if (absl::Symbolize(stack, buf, buf_size)) {
- output.append(" ").append(buf).append("\n");
+ char **symbols = backtrace_symbols(local_stack.data(), frames);
+ if (symbols != nullptr) {
+ for (int i = 0; i < frames; ++i) {
+ output.append(" ").append(symbols[i]).append("\n");
}
+ std::free(symbols);
+ return output;
}
+ for (int i = 0; i < frames; ++i) {
+ std::ostringstream stream;
+ stream << " frame[" << i << "] " << local_stack[static_cast<size_t>(i)]
+ << "\n";
+ output.append(stream.str());
+ }
+ return output;
+#else
+ std::string output = " Stack trace is not supported on this platform.\n";
return output;
+#endif
}
std::unordered_map<ForyLogLevel, std::string> log_level_to_str = {
diff --git a/cpp/fory/util/logging_test.cc b/cpp/fory/util/logging_test.cc
index 6facd98ea..8525a9b9f 100644
--- a/cpp/fory/util/logging_test.cc
+++ b/cpp/fory/util/logging_test.cc
@@ -30,7 +30,8 @@ namespace fory {
TEST(PrintLogTest, BasicLog) {
FORY_LOG(FORY_INFO) << "test info";
ASSERT_DEATH(FORY_LOG(FORY_FATAL) << "test fatal",
- "\\[.*\\] FATAL cpp/fory/util/logging_test.cc:.*: test fatal");
+ "\\[.*\\] FATAL .*cpp/fory/util/logging_test.cc:.*: test "
+ "fatal");
}
TEST(PrintLogTest, TestCheckOp) {
@@ -71,12 +72,10 @@ std::string test_function_level2() { return
test_function_level1(); }
TEST(PrintLogTest, CallstackTraceTest) {
auto ret = test_function_level2();
FORY_LOG(FORY_INFO) << "stack trace:\n" << ret;
- // work for linux
- // EXPECT_TRUE(ret.find("test_function_level0") != std::string::npos);
- // work for mac
- // EXPECT_TRUE(ret.find("get_call_trace") != std::string::npos);
- EXPECT_TRUE(ret.find("fory") != std::string::npos);
- EXPECT_TRUE(ret.find("PrintLogTest") != std::string::npos);
+ EXPECT_FALSE(ret.empty());
+ EXPECT_TRUE(ret.find("0x") != std::string::npos ||
+ ret.find("test_function") != std::string::npos ||
+ ret.find("PrintLogTest") != std::string::npos);
}
#endif
diff --git a/LICENSE b/licenses/LICENSE-abseil-cpp.txt
similarity index 70%
copy from LICENSE
copy to licenses/LICENSE-abseil-cpp.txt
index 62e28df76..ccd61dcfe 100644
--- a/LICENSE
+++ b/licenses/LICENSE-abseil-cpp.txt
@@ -1,6 +1,7 @@
+
Apache License
Version 2.0, January 2004
- http://www.apache.org/licenses/
+ https://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
@@ -192,7 +193,7 @@
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
+ https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
@@ -200,105 +201,3 @@
See the License for the specific language governing permissions and
limitations under the License.
-================================================================
-
-This product includes a number of Dependencies with separate copyright notices
-and license terms. Your use of these submodules is subject to the terms and
-conditions of the following licenses.
-
-================================================================
-
-================================================================
-Apache-2.0 licenses
-================================================================
-The following components are provided under the Apache-2.0 License. See
project link for details.
-The text of each license is the standard Apache 2.0 license.
-
-* guava (https://github.com/google/guava)
- Files:
- java/fory-core/src/main/java/org/apache/fory/util/Preconditions.java
- java/fory-core/src/main/java/org/apache/fory/reflect/TypeParameter.java
- java/fory-core/src/main/java/org/apache/fory/reflect/TypeRef.java
-
java/fory-core/src/main/java/org/apache/fory/util/concurrency/DirectExecutorService.java
-
-* spark (https://github.com/apache/spark)
- Files:
- java/fory-core/src/main/java/org/apache/fory/codegen/Code.java
- java/fory-core/src/main/java/org/apache/fory/memory/Platform.java
-
java/fory-format/src/main/java/org/apache/fory/format/vectorized/ArrowWriter.java
-
-* commons-io (https://github.com/apache/commons-io)
- Files:
-
java/fory-core/src/main/java/org/apache/fory/io/ClassLoaderObjectInputStream.java
-
-* common-lang3 (https://github.com/apache/commons-lang)
- Files:
- java/fory-core/src/main/java/org/apache/fory/util/StringUtils.java
-
-* fast-serialization (https://github.com/RuedigerMoeller/fast-serialization)
- Files:
-
benchmarks/java/src/main/java/org/apache/fory/benchmark/Identity2IdMap.java
-
-* ray (https://github.com/ray-project/ray)
- Files:
- ci/format.sh
-
-* grpc (https://github.com/grpc/grpc)
- Files:
- bazel/cython_library.bzl
-
-
-================================================================
-BSD-3-Clause licenses
-================================================================
-The following components are provided under the BSD-3-Clause License. See
project link for details.
-The text of each license is also included in licenses/LICENSE-[project].txt.
-
-* kryo (https://github.com/EsotericSoftware/kryo)
- Files:
- benchmarks/java/src/main/java/org/apache/fory/benchmark/data/Data.java
- benchmarks/java/src/main/java/org/apache/fory/benchmark/data/Image.java
- benchmarks/java/src/main/java/org/apache/fory/benchmark/data/Media.java
-
benchmarks/java/src/main/java/org/apache/fory/benchmark/data/MediaContent.java
- benchmarks/java/src/main/java/org/apache/fory/benchmark/data/Sample.java
-
benchmarks/java/src/main/java/org/apache/fory/benchmark/state/BufferType.java
-
benchmarks/java/src/main/java/org/apache/fory/benchmark/state/KryoState.java
-
java/fory-core/src/main/java/org/apache/fory/collection/ForyObjectMap.java
- java/fory-core/src/main/java/org/apache/fory/collection/IdentityMap.java
-
java/fory-core/src/main/java/org/apache/fory/collection/IdentityObjectIntMap.java
- java/fory-core/src/main/java/org/apache/fory/collection/LongMap.java
-
java/fory-core/src/main/java/org/apache/fory/collection/LongLongByteMap.java
- java/fory-core/src/main/java/org/apache/fory/collection/ObjectIntMap.java
- java/fory-core/src/main/java/org/apache/fory/type/Generics.java
- java/fory-core/src/test/java/org/apache/fory/type/GenericsTest.java
-
-================================================================
-MIT licenses
-================================================================
-The following components are provided under the MIT License. See project link
for details.
-The text of each license is also included in licenses/LICENSE-[project].txt
-
-* murmurHash3.js (https://github.com/karanlyons/murmurHash3.js)
- Files:
- javascript/packages/fory/lib/murmurHash3.ts
-
-* cython-abseil-sample (https://github.com/messense/cython-abseil-sample)
- Files:
- python/pyfory/includes/libabsl.pxd
-
-
-================================================================
-Public Domain
-================================================================
-The following components are placed in the public domain.
-The author hereby disclaims copyright to this source code.
-See project link for details.
-
-* smhasher (https://github.com/aappleby/smhasher)
- Files:
- src/fory/thirdparty/MurmurHash3.cc
- src/fory/thirdparty/MurmurHash3.h
-
-* java_util (https://github.com/yonik/java_util)
- Files:
- java/fory-core/src/main/java/org/apache/fory/util/MurmurHash3.java
diff --git a/licenses/LICENSE-cython-abseil-example.txt
b/licenses/LICENSE-cython-abseil-example.txt
deleted file mode 100644
index 2e8b71311..000000000
--- a/licenses/LICENSE-cython-abseil-example.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2020 - present messense
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/python/pyfory/context.pxi b/python/pyfory/context.pxi
index 7aec0d11b..f3c94422a 100644
--- a/python/pyfory/context.pxi
+++ b/python/pyfory/context.pxi
@@ -68,7 +68,7 @@ cdef class RefWriter:
cdef inline bint write_ref_or_null(self, CBuffer * c_buffer, obj):
cdef uint64_t object_id
cdef int32_t next_id
- cdef flat_hash_map[uint64_t, int32_t].iterator it
+ cdef pair[uint64_t, int32_t] *entry
if not self.track_ref:
if obj is None:
deref(c_buffer).write_int8(NULL_FLAG)
@@ -79,8 +79,8 @@ cdef class RefWriter:
deref(c_buffer).write_int8(NULL_FLAG)
return True
object_id = <uintptr_t> <PyObject *> obj
- it = self.written_objects_id.find(object_id)
- if it == self.written_objects_id.end():
+ entry = self.written_objects_id.find(object_id)
+ if entry == NULL:
next_id = self.written_objects_id.size()
self.written_objects_id[object_id] = next_id
self.written_objects.push_back(<PyObject *> obj)
@@ -88,20 +88,20 @@ cdef class RefWriter:
deref(c_buffer).write_int8(REF_VALUE_FLAG)
return False
deref(c_buffer).write_int8(REF_FLAG)
- deref(c_buffer).write_var_uint32(<uint64_t> deref(it).second)
+ deref(c_buffer).write_var_uint32(<uint64_t> deref(entry).second)
return True
cdef inline bint write_ref_value_flag(self, CBuffer * c_buffer, obj):
cdef uint64_t object_id
cdef int32_t next_id
- cdef flat_hash_map[uint64_t, int32_t].iterator it
+ cdef pair[uint64_t, int32_t] *entry
assert obj is not None
if not self.track_ref:
deref(c_buffer).write_int8(NOT_NULL_VALUE_FLAG)
return True
object_id = <uintptr_t> <PyObject *> obj
- it = self.written_objects_id.find(object_id)
- if it == self.written_objects_id.end():
+ entry = self.written_objects_id.find(object_id)
+ if entry == NULL:
next_id = self.written_objects_id.size()
self.written_objects_id[object_id] = next_id
self.written_objects.push_back(<PyObject *> obj)
@@ -109,7 +109,7 @@ cdef class RefWriter:
deref(c_buffer).write_int8(REF_VALUE_FLAG)
return True
deref(c_buffer).write_int8(REF_FLAG)
- deref(c_buffer).write_var_uint32(<uint64_t> deref(it).second)
+ deref(c_buffer).write_var_uint32(<uint64_t> deref(entry).second)
return False
cdef inline bint write_null_flag(self, CBuffer * c_buffer, obj):
@@ -281,8 +281,8 @@ cdef class MetaStringWriter:
cdef uint64_t object_id = <uintptr_t> <PyObject *> encoded_meta_string
cdef int32_t length = encoded_meta_string.length
cdef int32_t dynamic_id
- cdef flat_hash_map[uint64_t, int32_t].iterator it =
self._written_encoded_meta_strings.find(object_id)
- if it == self._written_encoded_meta_strings.end():
+ cdef pair[uint64_t, int32_t] *entry =
self._written_encoded_meta_strings.find(object_id)
+ if entry == NULL:
dynamic_id = self._written_encoded_meta_strings.size()
self._written_encoded_meta_strings[object_id] = dynamic_id
self._written_objects.push_back(<PyObject *> encoded_meta_string)
@@ -295,7 +295,7 @@ cdef class MetaStringWriter:
buffer.write_int64(encoded_meta_string.hashcode)
buffer.write_bytes(encoded_meta_string.data)
return
- buffer.write_var_uint32(((deref(it).second + 1) << 1) | 1)
+ buffer.write_var_uint32(((deref(entry).second + 1) << 1) | 1)
cpdef inline reset(self):
cdef PyObject *item
@@ -326,6 +326,7 @@ cdef class MetaStringReader:
cdef int8_t encoding = 0
cdef bytes data
cdef object encoded_meta_string
+ cdef pair[int64_t, PyObject *] *entry
if header & 0b1:
if length <= 0:
raise ValueError("Invalid dynamic metastring id 0")
@@ -344,8 +345,8 @@ cdef class MetaStringReader:
v1 = buffer.read_int64()
v2 = buffer.read_bytes_as_int64(length - 8)
hashcode = _hash_small_metastring(v1, v2, length, <uint8_t>
encoding)
- encoded_meta_string_ptr =
self._c_hash_to_small_encoded_meta_string[hashcode]
- if encoded_meta_string_ptr == NULL:
+ entry = self._c_hash_to_small_encoded_meta_string.find(hashcode)
+ if entry == NULL or deref(entry).second == NULL:
reader_index = buffer.get_reader_index()
data = buffer.get_bytes(reader_index - length, length)
encoded_meta_string =
self.shared_registry.get_or_create_encoded_meta_string(
@@ -354,13 +355,15 @@ cdef class MetaStringReader:
)
encoded_meta_string_ptr = <PyObject *> encoded_meta_string
self._c_hash_to_small_encoded_meta_string[hashcode] =
encoded_meta_string_ptr
+ else:
+ encoded_meta_string_ptr = deref(entry).second
else:
hashcode = buffer.read_int64()
reader_index = buffer.get_reader_index()
buffer.check_bound(reader_index, length)
buffer.set_reader_index(reader_index + length)
- encoded_meta_string_ptr =
self._c_hash_to_encoded_meta_string[hashcode]
- if encoded_meta_string_ptr == NULL:
+ entry = self._c_hash_to_encoded_meta_string.find(hashcode)
+ if entry == NULL or deref(entry).second == NULL:
data = buffer.get_bytes(reader_index, length)
encoded_meta_string =
self.shared_registry.get_or_create_encoded_meta_string(
data,
@@ -368,6 +371,8 @@ cdef class MetaStringReader:
)
encoded_meta_string_ptr = <PyObject *> encoded_meta_string
self._c_hash_to_encoded_meta_string[hashcode] =
encoded_meta_string_ptr
+ else:
+ encoded_meta_string_ptr = deref(entry).second
self._c_dynamic_id_to_encoded_meta_string_vec.push_back(encoded_meta_string_ptr)
return <object> encoded_meta_string_ptr
diff --git a/python/pyfory/includes/libabsl.pxd
b/python/pyfory/includes/libabsl.pxd
deleted file mode 100644
index 906cc0b6b..000000000
--- a/python/pyfory/includes/libabsl.pxd
+++ /dev/null
@@ -1,75 +0,0 @@
-from libcpp.utility cimport pair
-
-# Copied from
https://github.com/messense/cython-abseil-sample/blob/master/absl.pxd
-cdef extern from "absl/container/flat_hash_map.h" namespace "absl" nogil:
- cdef cppclass flat_hash_map[T, U]:
- ctypedef T key_type
- ctypedef U mapped_type
- ctypedef pair[const T, U] value_type
- cppclass iterator:
- pair[T, U]& operator*()
- iterator operator++()
- iterator operator--()
- bint operator==(iterator)
- bint operator!=(iterator)
- cppclass reverse_iterator:
- pair[T, U]& operator*()
- iterator operator++()
- iterator operator--()
- bint operator==(reverse_iterator)
- bint operator!=(reverse_iterator)
- cppclass const_iterator(iterator):
- pass
- cppclass const_reverse_iterator(reverse_iterator):
- pass
- flat_hash_map() except +
- flat_hash_map(flat_hash_map&) except +
- # flat_hash_map(key_compare&)
- U& operator[](T&)
- # flat_hash_map& operator=(flat_hash_map&)
- bint operator==(flat_hash_map&, flat_hash_map&)
- bint operator!=(flat_hash_map&, flat_hash_map&)
- bint operator<(flat_hash_map&, flat_hash_map&)
- bint operator>(flat_hash_map&, flat_hash_map&)
- bint operator<=(flat_hash_map&, flat_hash_map&)
- bint operator>=(flat_hash_map&, flat_hash_map&)
- U& at(const T&)
- const U& const_at "at"(const T&)
- iterator begin()
- const_iterator const_begin "begin"()
- void clear()
- size_t count(T&)
- bint empty()
- iterator end()
- const_iterator const_end "end"()
- pair[iterator, iterator] equal_range(T&)
- pair[const_iterator, const_iterator] const_equal_range
"equal_range"(const T&)
- void erase(iterator)
- void erase(iterator, iterator)
- size_t erase(T&)
- iterator find(T&)
- const_iterator const_find "find"(T&)
- pair[iterator, bint] insert(pair[T, U]) # XXX pair[T,U]&
- iterator insert(iterator, pair[T, U]) # XXX pair[T,U]&
- iterator insert(iterator, iterator)
- # key_compare key_comp()
- iterator lower_bound(T&)
- const_iterator const_lower_bound "lower_bound"(T&)
- size_t max_size()
- reverse_iterator rbegin()
- const_reverse_iterator const_rbegin "rbegin"()
- reverse_iterator rend()
- const_reverse_iterator const_rend "rend"()
- size_t size()
- void swap(flat_hash_map&)
- iterator upper_bound(T&)
- const_iterator const_upper_bound "upper_bound"(T&)
- # value_compare value_comp()
- void max_load_factor(float)
- float max_load_factor()
- void rehash(size_t)
- void reserve(size_t)
- size_t bucket_count()
- size_t max_bucket_count()
- size_t bucket_size(size_t)
- size_t bucket(const T&)
diff --git a/cpp/cmake/ForyConfig.cmake.in
b/python/pyfory/includes/libflat_hash_map.pxd
similarity index 54%
copy from cpp/cmake/ForyConfig.cmake.in
copy to python/pyfory/includes/libflat_hash_map.pxd
index f14844247..5cc39d6d0 100644
--- a/cpp/cmake/ForyConfig.cmake.in
+++ b/python/pyfory/includes/libflat_hash_map.pxd
@@ -14,26 +14,25 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
+#
-@PACKAGE_INIT@
-
-include(CMakeFindDependencyMacro)
-
-# Check for Abseil (optional)
-find_package(absl CONFIG QUIET)
-
-include("${CMAKE_CURRENT_LIST_DIR}/ForyTargets.cmake")
-
-# Provide convenient component targets
-# Users can use:
-# find_package(Fory REQUIRED)
-# target_link_libraries(myapp fory::serialization) # For serialization only
-# target_link_libraries(myapp fory::row_format) # For row format
-# target_link_libraries(myapp fory::fory) # For everything
-
-check_required_components(Fory)
+from libcpp.utility cimport pair
-# Print found message
-if(NOT Fory_FIND_QUIETLY)
- message(STATUS "Found Fory: ${CMAKE_CURRENT_LIST_DIR}")
-endif()
+cdef extern from "fory/thirdparty/flat_hash_map.h" namespace "fory" nogil:
+ cdef cppclass flat_hash_map[T, U]:
+ ctypedef T key_type
+ ctypedef U mapped_type
+ ctypedef pair[T, U] value_type
+ flat_hash_map() except +
+ flat_hash_map(flat_hash_map&) except +
+ U& operator[](const T&)
+ void clear()
+ bint empty()
+ pair[T, U]* find(const T&)
+ size_t size()
+ void swap(flat_hash_map&)
+ void max_load_factor(float)
+ float max_load_factor()
+ void rehash(size_t)
+ void reserve(size_t)
+ size_t bucket_count()
diff --git a/python/pyfory/serialization.pyx b/python/pyfory/serialization.pyx
index c9e85de3c..375fa9882 100644
--- a/python/pyfory/serialization.pyx
+++ b/python/pyfory/serialization.pyx
@@ -34,7 +34,7 @@ from cpython.dict cimport PyDict_Next
from cpython.list cimport PyList_New, PyList_SET_ITEM
from cpython.tuple cimport PyTuple_New, PyTuple_SET_ITEM
from cpython.ref cimport Py_INCREF, Py_XDECREF
-from pyfory.includes.libabsl cimport flat_hash_map
+from pyfory.includes.libflat_hash_map cimport flat_hash_map
from pyfory.includes.libutil cimport FlatIntMap
from pyfory._fory import (
NO_USER_TYPE_ID,
@@ -315,8 +315,11 @@ cdef class TypeResolver:
self._populate_type_info(typeinfo)
cpdef inline TypeInfo get_type_info(self, cls, create=True):
- cdef PyObject * typeinfo_ptr = self._c_types_info[<uintptr_t>
<PyObject *> cls]
+ cdef pair[uint64_t, PyObject *] *entry =
self._c_types_info.find(<uintptr_t> <PyObject *> cls)
+ cdef PyObject * typeinfo_ptr = NULL
cdef TypeInfo typeinfo
+ if entry != NULL:
+ typeinfo_ptr = deref(entry).second
if typeinfo_ptr != NULL:
typeinfo = <TypeInfo> typeinfo_ptr
if typeinfo.serializer is None:
@@ -388,6 +391,7 @@ cdef class TypeResolver:
cdef object ns_metabytes
cdef object type_metabytes
cdef PyObject * typeinfo_ptr = NULL
+ cdef pair[uint32_t, PyObject *] *entry
if (
type_id == <uint8_t>TypeId.COMPATIBLE_STRUCT
or type_id == <uint8_t>TypeId.NAMED_COMPATIBLE_STRUCT
@@ -417,10 +421,10 @@ cdef class TypeResolver:
return self._load_bytes_to_type_info(ns_metabytes, type_metabytes)
if reg_kind == TypeRegistrationKind.BY_ID:
user_type_id = buffer.read_var_uint32()
- typeinfo_ptr = self._c_user_type_id_to_type_info[user_type_id]
- if typeinfo_ptr == NULL:
+ entry = self._c_user_type_id_to_type_info.find(user_type_id)
+ if entry == NULL or deref(entry).second == NULL:
raise ValueError(f"Unexpected user_type_id {user_type_id}")
- return <TypeInfo>typeinfo_ptr
+ return <TypeInfo>deref(entry).second
if type_id >= self._c_registered_id_to_type_info.size():
raise ValueError(f"Unexpected type_id {type_id}")
typeinfo_ptr = self._c_registered_id_to_type_info[type_id]
@@ -468,7 +472,7 @@ cdef class TypeResolver:
cdef uint8_t type_id
cdef object type_def
cdef uint64_t type_addr
- cdef flat_hash_map[uint64_t, int32_t].iterator it
+ cdef pair[uint64_t, int32_t] *entry
cdef int32_t index
if meta_context is None:
raise AssertionError(
@@ -481,9 +485,9 @@ cdef class TypeResolver:
write_context.write_bytes(typeinfo.type_def.encoded)
return
type_addr = <uint64_t> <PyObject *> type_cls
- it = meta_context.class_map.find(type_addr)
- if it != meta_context.class_map.end():
- write_context.write_var_uint32((deref(it).second << 1) | 1)
+ entry = meta_context.class_map.find(type_addr)
+ if entry != NULL:
+ write_context.write_var_uint32((deref(entry).second << 1) | 1)
return
index = meta_context.class_map.size()
meta_context.class_map[type_addr] = index
@@ -526,21 +530,17 @@ cdef class TypeResolver:
return typeinfo
cdef inline TypeInfo _load_bytes_to_type_info(self, object ns_metabytes,
object type_metabytes):
- cdef PyObject * typeinfo_ptr = self._c_meta_hash_to_type_info[
- pair[int64_t, int64_t](
- ns_metabytes.hashcode,
- type_metabytes.hashcode,
- )
- ]
+ cdef pair[int64_t, int64_t] hash_key = pair[int64_t, int64_t](
+ ns_metabytes.hashcode,
+ type_metabytes.hashcode,
+ )
+ cdef pair[pair[int64_t, int64_t], PyObject *] *entry =
self._c_meta_hash_to_type_info.find(hash_key)
cdef TypeInfo typeinfo
- if typeinfo_ptr != NULL:
- return <TypeInfo>typeinfo_ptr
+ if entry != NULL and deref(entry).second != NULL:
+ return <TypeInfo>deref(entry).second
typeinfo = self.resolver._load_metabytes_to_type_info(ns_metabytes,
type_metabytes)
self._c_meta_hash_to_type_info[
- pair[int64_t, int64_t](
- ns_metabytes.hashcode,
- type_metabytes.hashcode,
- )
+ hash_key
] = <PyObject *>typeinfo
return typeinfo
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]