Skip to content

feat: implement JsonSerializable and __serialize/__unserialize#41

Merged
orieg merged 1 commit intomainfrom
feat/serialization
Feb 28, 2026
Merged

feat: implement JsonSerializable and __serialize/__unserialize#41
orieg merged 1 commit intomainfrom
feat/serialization

Conversation

@orieg
Copy link
Owner

@orieg orieg commented Feb 28, 2026

Summary

  • Implements JsonSerializable interface so json_encode($judy) works natively, returning appropriate JSON format per type (flat array for BITSET, associative object for INT_TO_INT, INT_TO_MIXED, STRING_TO_INT, STRING_TO_MIXED)
  • Implements __serialize() / __unserialize() for native PHP serialize() / unserialize() support with type-preserving roundtrip across all 5 Judy array types
  • Shared judy_build_data_array() helper avoids code duplication between jsonSerialize and __serialize
  • Adds json extension dependency to module deps (judy_deps[]), config.m4, and config.w32
  • 12 new .phpt test files covering all types, empty arrays, roundtrip integrity, and error handling
  • Checks off Serialization and JsonSerializable items in README roadmap

Implementation details

JsonSerializable output format

Type JSON output
BITSET [1, 5, 10] (flat array of set indices)
INT_TO_INT {"0": 100, "5": 200} (index => value)
INT_TO_MIXED {"0": "hello", "5": [1,2]} (index => mixed)
STRING_TO_INT {"foo": 100} (key => value)
STRING_TO_MIXED {"foo": "bar"} (key => mixed)

Serialization format

__serialize() returns ['type' => int, 'data' => array] where data matches the jsonSerialize() format. __unserialize() validates type, reconstructs the Judy array via judy_object_write_dimension_helper().

Test plan

  • 85/85 tests pass (Docker PHP 8.3)
  • CI matrix (PHP 8.1-8.5, Linux + Windows)
  • Verify pecl package package.xml produces valid .tgz

Add modern serialization support to Judy arrays:
- JsonSerializable interface: json_encode() returns appropriate format
  per type (flat array for BITSET, associative for others)
- __serialize/__unserialize: native PHP serialize()/unserialize() support
  with type-preserving roundtrip for all 5 Judy array types
- Shared judy_build_data_array() helper to avoid code duplication
- json extension dependency added to module deps and build configs
- 12 new test files covering all types, empty arrays, and error handling
@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the Judy extension by integrating standard PHP serialization and JSON serialization capabilities. It allows Judy arrays to be natively serialized using serialize() and json_encode(), improving interoperability with other PHP components and data formats. The changes ensure that Judy arrays can be easily persisted and exchanged while maintaining their type and data integrity.

Highlights

  • JsonSerializable Implementation: The Judy class now implements the JsonSerializable interface, allowing native json_encode() support for Judy arrays. The output format varies by Judy array type, producing flat arrays for BITSET and associative objects for other types (INT_TO_INT, INT_TO_MIXED, STRING_TO_INT, STRING_TO_MIXED).
  • Native PHP Serialization Support: Implemented __serialize() and __unserialize() magic methods for the Judy class, enabling seamless native PHP serialize() and unserialize() functionality. This ensures type-preserving roundtrips for all five Judy array types.
  • Code Reusability: A new helper function, judy_build_data_array(), was introduced to consolidate the logic for converting Judy array contents into a PHP array, reducing code duplication between jsonSerialize() and __serialize().
  • Dependency Update: The judy extension now explicitly declares a dependency on the json extension in config.m4, config.w32, and php_judy.c to ensure proper functionality of the new JSON serialization features.
  • Comprehensive Test Coverage: Twelve new .phpt test files have been added to thoroughly cover all Judy array types, empty arrays, roundtrip integrity for serialization, and error handling for __unserialize().
  • Roadmap Progress: The README.md file has been updated to mark 'Serialization' and 'Interoperability: Implement JsonSerializable interface' as completed items on the project roadmap.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • README.md
    • Updated the roadmap to mark Serialization and JsonSerializable as completed.
  • config.m4
    • Added a dependency on the 'json' extension using PHP_ADD_EXTENSION_DEP.
  • config.w32
    • Added a dependency on the 'json' extension using ADD_EXTENSION_DEP.
  • package.xml
    • Added 12 new .phpt test files for JSON serialization and native PHP serialization.
  • php_judy.c
    • Included the php_json.h header for JSON functionality.
    • Implemented the JsonSerializable interface for the Judy class.
    • Added a static helper function judy_build_data_array to construct PHP arrays from Judy array contents.
    • Implemented the jsonSerialize method to return a PHP array suitable for JSON encoding.
    • Implemented the __serialize method to return an array containing the Judy type and data for serialization.
    • Implemented the __unserialize method to reconstruct a Judy array from serialized data, including validation and error handling.
    • Declared the new jsonSerialize, __serialize, and __unserialize methods in judy_class_methods.
    • Added a module dependency on the 'json' extension in judy_deps.
  • tests/json_serialize_bitset_001.phpt
    • Added a test for Judy::BITSET JSON serialization, verifying flat array output.
  • tests/json_serialize_empty_001.phpt
    • Added a test for JSON serialization of empty Judy arrays across all types.
  • tests/json_serialize_int_to_int_001.phpt
    • Added a test for Judy::INT_TO_INT JSON serialization, verifying index-to-value object output.
  • tests/json_serialize_int_to_mixed_001.phpt
    • Added a test for Judy::INT_TO_MIXED JSON serialization, verifying index-to-mixed-value object output.
  • tests/json_serialize_string_to_int_001.phpt
    • Added a test for Judy::STRING_TO_INT JSON serialization, verifying key-to-value object output.
  • tests/json_serialize_string_to_mixed_001.phpt
    • Added a test for Judy::STRING_TO_MIXED JSON serialization, verifying key-to-mixed-value object output.
  • tests/serialize_error_001.phpt
    • Added tests for __unserialize error handling with invalid serialization data.
  • tests/serialize_roundtrip_bitset_001.phpt
    • Added a test for Judy::BITSET serialization and unserialization roundtrip integrity.
  • tests/serialize_roundtrip_int_to_int_001.phpt
    • Added a test for Judy::INT_TO_INT serialization and unserialization roundtrip integrity.
  • tests/serialize_roundtrip_int_to_mixed_001.phpt
    • Added a test for Judy::INT_TO_MIXED serialization and unserialization roundtrip integrity.
  • tests/serialize_roundtrip_string_to_int_001.phpt
    • Added a test for Judy::STRING_TO_INT serialization and unserialization roundtrip integrity.
  • tests/serialize_roundtrip_string_to_mixed_001.phpt
    • Added a test for Judy::STRING_TO_MIXED serialization and unserialization roundtrip integrity.
Activity
  • The pull request author, orieg, created this pull request to introduce new serialization features.
  • The author provided a detailed summary of the changes, implementation details, and a test plan in the pull request description.
  • No human review comments or further activity have been recorded yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions
Copy link

Test Results

PHP Platform Arch TS Tests Pass Fail Skip Duration
8.1 Linux x64 - 85 85 0 0 0.7s
8.2 Linux x64 - 85 85 0 0 0.7s
8.3 Linux x64 - 85 85 0 0 1.0s
8.4 Linux x64 - 85 85 0 0 1.1s
8.5 Linux x64 - 85 85 0 0 1.0s
8.1 Windows x64 nts 85 85 0 0 5.2s
8.2 Windows x64 nts 85 85 0 0 5.2s
8.3 Windows x64 nts 85 85 0 0 5.2s
8.4 Windows x64 nts 85 85 0 0 5.2s
8.5 Windows x64 nts 85 85 0 0 5.5s
Total 850 850 0 0

Benchmark Summary (Judy vs PHP Array)

Ratio = Judy / Array. Bold = Judy wins (≤0.95x). Plain = Array is faster/smaller.

Time (Write / Read) — Linux

Scenario PHP 8.1 PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5
Sparse Int 100K 3.1x / 2.6x 2.5x / 3.0x 3.0x / 2.6x 2.8x / 2.7x 2.9x / 2.5x
Sparse Int 500K 3.1x / 2.6x 3.2x / 4.1x 2.7x / 2.1x 3.3x / 2.5x 3.3x / 2.7x
Sparse Int 1M 3.6x / 3.1x 3.6x / 4.9x 2.5x / 2.1x 1.8x / 2.3x 2.9x / 2.4x
Sparse Int 10M 1.9x / 2.3x 3.1x / 4.8x 1.8x / 2.5x 2.0x / 2.6x 1.8x / 2.3x
String 100K 3.6x / 3.0x 2.9x / 5.3x 2.1x / 2.7x 2.1x / 1.9x 3.4x / 3.1x
String 500K 2.6x / 2.8x 3.5x / 4.1x 2.4x / 1.9x 2.2x / 2.0x 2.1x / 2.3x
String 1M 2.5x / 2.7x 3.5x / 4.1x 2.6x / 1.9x 2.4x / 1.9x 2.4x / 1.9x
String 10M 2.8x / 2.4x 3.3x / 5.1x 2.9x / 2.6x 2.8x / 2.5x 2.7x / 2.5x

Memory — Linux

Scenario PHP 8.1 PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5
Sparse Int 100K 0.26x 0.26x 0.26x 0.26x 0.26x
Sparse Int 500K 0.46x 0.46x 0.46x 0.46x 0.46x
Sparse Int 1M 0.46x 0.46x 0.46x 0.46x 0.46x
Sparse Int 10M 0.29x 0.29x 0.29x 0.29x 0.29x
String 100K 0.61x 0.61x 0.61x 0.61x 0.61x
String 500K 0.76x 0.76x 0.76x 0.76x 0.76x
String 1M 0.76x 0.76x 0.76x 0.76x 0.76x
String 10M 0.48x 0.48x 0.48x 0.48x 0.48x

Time (Write / Read) — Windows

Scenario PHP 8.1 PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5
Sparse Int 100K 5.2x / 3.5x 5.5x / 2.8x 4.4x / 2.3x 5.4x / 3.0x 5.1x / 3.8x
Sparse Int 500K 4.4x / 3.3x 4.9x / 2.9x 4.6x / 2.7x 5.0x / 2.8x 4.4x / 3.3x
Sparse Int 1M 4.2x / 2.4x 4.0x / 2.6x 4.2x / 2.6x 4.0x / 2.7x 4.4x / 3.1x
Sparse Int 10M 3.7x / 2.8x 2.4x / 2.1x 2.3x / 2.1x 2.4x / 2.2x 3.6x / 2.7x
String 100K 4.8x / 6.4x 4.5x / 4.0x 4.6x / 4.0x 3.6x / 4.2x 4.9x / 6.1x
String 500K 4.2x / 3.5x 3.7x / 2.7x 3.8x / 2.6x 3.6x / 2.5x 4.2x / 2.8x
String 1M 4.5x / 3.4x 3.4x / 2.4x 3.3x / 2.4x 3.5x / 2.2x 4.6x / 2.7x
String 10M 4.4x / 3.9x 3.4x / 2.3x 3.5x / 2.3x 3.4x / 2.4x 4.4x / 3.1x

Memory — Windows

Scenario PHP 8.1 PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5
Sparse Int 100K 0.23x 0.23x 0.23x 0.23x 0.23x
Sparse Int 500K 0.46x 0.46x 0.46x 0.46x 0.46x
Sparse Int 1M 0.46x 0.46x 0.46x 0.46x 0.46x
Sparse Int 10M 0.29x 0.29x 0.29x 0.29x 0.29x
String 100K 0.51x 0.51x 0.51x 0.51x 0.51x
String 500K 0.76x 0.76x 0.76x 0.76x 0.76x
String 1M 0.76x 0.76x 0.76x 0.76x 0.76x
String 10M 0.48x 0.48x 0.48x 0.48x 0.48x
Raw benchmark data

Write Time — Linux

Scenario Subject PHP 8.1 PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5
Sparse Int 100K Judy 0.0122s 0.0113s 0.0127s 0.0121s 0.0122s
Sparse Int 100K PHP Array 0.0040s 0.0046s 0.0043s 0.0043s 0.0042s
Sparse Int 500K Judy 0.0673s 0.0773s 0.0666s 0.0723s 0.0664s
Sparse Int 500K PHP Array 0.0214s 0.0238s 0.0248s 0.0218s 0.0201s
Sparse Int 1M Judy 0.1863s 0.1781s 0.2315s 0.1755s 0.1782s
Sparse Int 1M PHP Array 0.0511s 0.0500s 0.0939s 0.0997s 0.0616s
Sparse Int 10M Judy 2.8967s 3.7870s 3.5140s 3.6844s 2.9486s
Sparse Int 10M PHP Array 1.5309s 1.2129s 1.9740s 1.8523s 1.6342s
String 100K Judy 0.0213s 0.0286s 0.0281s 0.0306s 0.0229s
String 100K PHP Array 0.0059s 0.0097s 0.0134s 0.0143s 0.0067s
String 500K Judy 0.1853s 0.1868s 0.2076s 0.2047s 0.1680s
String 500K PHP Array 0.0710s 0.0539s 0.0880s 0.0923s 0.0810s
String 1M Judy 0.3773s 0.4331s 0.4863s 0.4386s 0.4057s
String 1M PHP Array 0.1534s 0.1241s 0.1869s 0.1853s 0.1670s
String 10M Judy 5.6894s 6.4276s 6.3348s 6.1980s 5.6527s
String 10M PHP Array 2.0426s 1.9214s 2.2179s 2.2313s 2.0937s

Read Time — Linux

Scenario Subject PHP 8.1 PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5
Sparse Int 100K Judy 0.0097s 0.0096s 0.0099s 0.0096s 0.0094s
Sparse Int 100K PHP Array 0.0037s 0.0032s 0.0038s 0.0036s 0.0037s
Sparse Int 500K Judy 0.0571s 0.0992s 0.0841s 0.0918s 0.0561s
Sparse Int 500K PHP Array 0.0222s 0.0244s 0.0401s 0.0362s 0.0205s
Sparse Int 1M Judy 0.1876s 0.2455s 0.2828s 0.2751s 0.1741s
Sparse Int 1M PHP Array 0.0602s 0.0503s 0.1334s 0.1197s 0.0718s
Sparse Int 10M Judy 3.3894s 4.1627s 3.9348s 4.1368s 3.4176s
Sparse Int 10M PHP Array 1.4718s 0.8686s 1.5986s 1.5761s 1.4581s
String 100K Judy 0.0163s 0.0276s 0.0231s 0.0271s 0.0163s
String 100K PHP Array 0.0054s 0.0052s 0.0085s 0.0139s 0.0052s
String 500K Judy 0.1871s 0.2209s 0.2235s 0.2295s 0.1696s
String 500K PHP Array 0.0673s 0.0534s 0.1187s 0.1122s 0.0722s
String 1M Judy 0.4380s 0.4400s 0.4945s 0.4671s 0.3929s
String 1M PHP Array 0.1639s 0.1071s 0.2625s 0.2439s 0.2052s
String 10M Judy 5.9870s 6.8405s 7.1364s 6.6969s 6.4266s
String 10M PHP Array 2.5199s 1.3461s 2.7749s 2.6626s 2.5900s

Memory — Linux

Scenario Subject PHP 8.1 PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5
Sparse Int 100K Judy 1.84 mb 1.85 mb 1.84 mb 1.84 mb 1.85 mb
Sparse Int 100K PHP Array 7 mb 7 mb 7 mb 7 mb 7 mb
Sparse Int 500K Judy 9.2 mb 9.2 mb 9.18 mb 9.17 mb 9.17 mb
Sparse Int 500K PHP Array 20 mb 20 mb 20 mb 20 mb 20 mb
Sparse Int 1M Judy 18.37 mb 18.37 mb 18.37 mb 18.38 mb 18.36 mb
Sparse Int 1M PHP Array 40 mb 40 mb 40 mb 40 mb 40 mb
Sparse Int 10M Judy 183.58 mb 183.59 mb 183.52 mb 183.62 mb 183.59 mb
Sparse Int 10M PHP Array 640 mb 640 mb 640 mb 640 mb 640 mb
String 100K Judy 3.05 mb 3.05 mb 3.05 mb 3.05 mb 3.05 mb
String 100K PHP Array 5 mb 5 mb 5 mb 5 mb 5 mb
String 500K Judy 15.26 mb 15.26 mb 15.26 mb 15.26 mb 15.26 mb
String 500K PHP Array 20 mb 20 mb 20 mb 20 mb 20 mb
String 1M Judy 30.52 mb 30.52 mb 30.52 mb 30.52 mb 30.52 mb
String 1M PHP Array 40 mb 40 mb 40 mb 40 mb 40 mb
String 10M Judy 305.18 mb 305.18 mb 305.18 mb 305.18 mb 305.18 mb
String 10M PHP Array 640 mb 640 mb 640 mb 640 mb 640 mb

Write Time — Windows

Scenario Subject PHP 8.1 PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5
Sparse Int 100K Judy 0.0325s 0.0286s 0.0290s 0.0289s 0.0299s
Sparse Int 100K PHP Array 0.0063s 0.0052s 0.0066s 0.0054s 0.0059s
Sparse Int 500K Judy 0.1776s 0.1465s 0.1504s 0.1499s 0.1679s
Sparse Int 500K PHP Array 0.0401s 0.0297s 0.0327s 0.0300s 0.0384s
Sparse Int 1M Judy 0.4135s 0.3054s 0.3111s 0.3265s 0.3842s
Sparse Int 1M PHP Array 0.0992s 0.0762s 0.0733s 0.0823s 0.0877s
Sparse Int 10M Judy 6.3074s 4.5656s 4.8318s 4.8195s 5.9532s
Sparse Int 10M PHP Array 1.6944s 1.9075s 2.0774s 2.0265s 1.6561s
String 100K Judy 0.0595s 0.0441s 0.0469s 0.0483s 0.0579s
String 100K PHP Array 0.0125s 0.0097s 0.0102s 0.0135s 0.0119s
String 500K Judy 0.3849s 0.2906s 0.3090s 0.3098s 0.3377s
String 500K PHP Array 0.0918s 0.0782s 0.0824s 0.0851s 0.0799s
String 1M Judy 0.8240s 0.6158s 0.6472s 0.6704s 0.7668s
String 1M PHP Array 0.1820s 0.1825s 0.1947s 0.1912s 0.1676s
String 10M Judy 11.6499s 8.3939s 9.1624s 8.9158s 10.9479s
String 10M PHP Array 2.6665s 2.4991s 2.6471s 2.6086s 2.5124s

Read Time — Windows

Scenario Subject PHP 8.1 PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5
Sparse Int 100K Judy 0.0173s 0.0156s 0.0155s 0.0154s 0.0177s
Sparse Int 100K PHP Array 0.0050s 0.0055s 0.0066s 0.0051s 0.0046s
Sparse Int 500K Judy 0.1488s 0.0853s 0.0866s 0.0878s 0.1242s
Sparse Int 500K PHP Array 0.0456s 0.0298s 0.0325s 0.0318s 0.0379s
Sparse Int 1M Judy 0.3249s 0.1919s 0.2084s 0.2215s 0.2984s
Sparse Int 1M PHP Array 0.1337s 0.0727s 0.0815s 0.0819s 0.0958s
Sparse Int 10M Judy 4.8891s 3.6650s 3.8213s 3.9160s 4.5324s
Sparse Int 10M PHP Array 1.7164s 1.7254s 1.8072s 1.7619s 1.6619s
String 100K Judy 0.0472s 0.0270s 0.0272s 0.0297s 0.0436s
String 100K PHP Array 0.0074s 0.0067s 0.0068s 0.0071s 0.0071s
String 500K Judy 0.2771s 0.1941s 0.2076s 0.2182s 0.2428s
String 500K PHP Array 0.0787s 0.0717s 0.0799s 0.0856s 0.0861s
String 1M Judy 0.5984s 0.4660s 0.4892s 0.5139s 0.5704s
String 1M PHP Array 0.1735s 0.1926s 0.2066s 0.2321s 0.2077s
String 10M Judy 8.5180s 6.7304s 7.2432s 7.2770s 7.9569s
String 10M PHP Array 2.1809s 2.9912s 3.1587s 3.0237s 2.5879s

Memory — Windows

Scenario Subject PHP 8.1 PHP 8.2 PHP 8.3 PHP 8.4 PHP 8.5
Sparse Int 100K Judy 1.84 mb 1.85 mb 1.84 mb 1.84 mb 1.84 mb
Sparse Int 100K PHP Array 8 mb 8 mb 8 mb 8 mb 8 mb
Sparse Int 500K Judy 9.18 mb 9.18 mb 9.2 mb 9.19 mb 9.18 mb
Sparse Int 500K PHP Array 20 mb 20 mb 20 mb 20 mb 20 mb
Sparse Int 1M Judy 18.32 mb 18.37 mb 18.39 mb 18.39 mb 18.36 mb
Sparse Int 1M PHP Array 40 mb 40 mb 40 mb 40 mb 40 mb
Sparse Int 10M Judy 183.6 mb 183.5 mb 183.6 mb 183.63 mb 183.61 mb
Sparse Int 10M PHP Array 640 mb 640 mb 640 mb 640 mb 640 mb
String 100K Judy 3.05 mb 3.05 mb 3.05 mb 3.05 mb 3.05 mb
String 100K PHP Array 6 mb 6 mb 6 mb 6 mb 6 mb
String 500K Judy 15.26 mb 15.26 mb 15.26 mb 15.26 mb 15.26 mb
String 500K PHP Array 20 mb 20 mb 20 mb 20 mb 20 mb
String 1M Judy 30.52 mb 30.52 mb 30.52 mb 30.52 mb 30.52 mb
String 1M PHP Array 40 mb 40 mb 40 mb 40 mb 40 mb
String 10M Judy 305.18 mb 305.18 mb 305.18 mb 305.18 mb 305.18 mb
String 10M PHP Array 640 mb 640 mb 640 mb 640 mb 640 mb

@orieg orieg merged commit f43c378 into main Feb 28, 2026
13 checks passed
@orieg orieg deleted the feat/serialization branch February 28, 2026 01:03
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements JsonSerializable and __serialize/__unserialize for the Judy class, enabling native JSON and PHP serialization. A critical memory leak was identified in the __unserialize method when called on an already-initialized object, which could lead to Denial of Service via memory exhaustion. Additionally, other memory leaks related to incorrect reference counting when serializing mixed-value types were found. Please see the detailed comments for suggestions on how to fix these issues.

JLF(PValue, intern->array, index);
while (PValue != NULL && PValue != PJERR) {
zval *value = JUDY_MVAL_READ(PValue);
Z_TRY_ADDREF_P(value);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The add_index_zval function on the next line already handles incrementing the reference count of the zval being added to the array. This explicit Z_TRY_ADDREF_P(value) call is therefore redundant and causes a double increment, leading to a memory leak. When the array returned by jsonSerialize or __serialize is destroyed, the values within it will not be freed due to the incorrect refcount.

JSLF(PValue, intern->array, key);
while (PValue != NULL && PValue != PJERR) {
zval *value = JUDY_MVAL_READ(PValue);
Z_TRY_ADDREF_P(value);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

Similar to the INT_TO_MIXED case, the add_assoc_zval function on the next line already handles incrementing the reference count. This explicit Z_TRY_ADDREF_P(value) call is redundant and will cause a memory leak. Please remove it.

Comment on lines +1975 to +1976
intern->type = jtype;
intern->array = (Pvoid_t) NULL;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The __unserialize method overwrites the internal Judy array pointer (intern->array) and type (intern->type) without freeing the existing array if the object was already initialized. This can be triggered by calling __unserialize manually on an existing Judy object, leading to a memory leak that could be exploited for Denial of Service via memory exhaustion. Simply setting intern->array = NULL on line 1976 leaks the memory of the previous Judy array. To fix this, call judy_free_array_internal(intern) before re-initializing the object's state. This helper function will also reset intern->array and intern->counter, making the subsequent assignments on lines 1976 and 1977 redundant.

judy_free_array_internal(intern);
intern->type = jtype;

orieg added a commit that referenced this pull request Feb 28, 2026
All four phases of the performance overhaul and feature extensions
(issue #37) are complete:
- Phase 1: Fast ZPP migration (PR #38)
- Phase 2: BITSET set operations (PR #39)
- Phase 3: Range slicing (PR #40)
- Phase 4: JsonSerializable + serialize/unserialize (PR #41)
@orieg orieg mentioned this pull request Feb 28, 2026
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant