pax_global_header00006660000000000000000000000064147025057220014516gustar00rootroot0000000000000052 comment=bccc52db5aa317a3c428b1a977dfa57be0c70bc2 python-frozendict/000077500000000000000000000000001470250572200145505ustar00rootroot00000000000000python-frozendict/.github/000077500000000000000000000000001470250572200161105ustar00rootroot00000000000000python-frozendict/.github/ISSUE_TEMPLATE/000077500000000000000000000000001470250572200202735ustar00rootroot00000000000000python-frozendict/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000006511470250572200227670ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: "[BUG] " labels: "bug" assignees: "" --- Welcome! You should write in your Bug Report: OS version (https://www.google.com/search?channel=fs&q=check+os+version&ie=utf-8&oe=utf-8): your OS version Python3 version (python3 -V -V): your Python version Steps to reproduce: 1. Actual result (with the python stack trace if present): the result you see python-frozendict/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000012561470250572200240240ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: "[FEATURE] " labels: "enhancement" assignees: "" --- Welcome! Before you write your Feature Request, please read below: **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. python-frozendict/.github/workflows/000077500000000000000000000000001470250572200201455ustar00rootroot00000000000000python-frozendict/.github/workflows/build_primary_wheels.yml000066400000000000000000000036631470250572200251110ustar00rootroot00000000000000name: Build primary wheels on: push: branches: - master tags: - v* pull_request: paths: - '**' jobs: build_wheels: name: Build primary wheels on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: include: - os: ubuntu-latest cibw_archs: "native" - os: windows-latest cibw_archs: "native" steps: - uses: actions/checkout@v3 - name: Build wheels uses: pypa/cibuildwheel@v2.19.2 env: CIBW_ARCHS: ${{ matrix.cibw_archs }} CIBW_SKIP: "cp311-* cp312-* pp*" CIBW_TEST_REQUIRES: pytest CIBW_TEST_COMMAND: > python -X faulthandler {package}/test/debug.py && python -X faulthandler -m pytest -p no:faulthandler -s {package} - uses: actions/upload-artifact@v3 with: path: ./wheelhouse/*.whl build_pure_py: name: Build and test pure py wheels runs-on: ubuntu-latest strategy: matrix: include: - tag: py311 version: 3.11 - tag: py312 version: 3.12 - tag: py313 version: 3.13 steps: - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "${{ matrix.version }}" - name: Install dependencies run: pip install -U pip setuptools wheel pytest pytest-cov - name: Build module run: FROZENDICT_PURE_PY=1 python setup.py bdist_wheel - name: Change tag to wheel run: python -m wheel tags --python-tag=${{ matrix.tag }} --remove dist/* - name: Install module run: pip install dist/* - name: Test with pytest run: pytest --cov=frozendict --cov-report=term-missing --cov-branch --cov-fail-under=100 - uses: actions/upload-artifact@v3 with: path: ./dist/* python-frozendict/.github/workflows/build_secondary_wheels.yml000066400000000000000000000041011470250572200254010ustar00rootroot00000000000000name: Build secondary wheels on: push: tags: - v* pull_request: paths: - '**' jobs: build_wheels: name: Build secondary wheels on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: include: - os: ubuntu-latest cibw_archs: "aarch64" - os: windows-latest cibw_archs: "ARM64" - os: macos-14 cibw_archs: "native" - os: macos-13 cibw_archs: "x86_64" steps: - name: Set up QEMU if: matrix.cibw_archs == 'aarch64' uses: docker/setup-qemu-action@v2 with: platforms: arm64 - uses: actions/checkout@v3 - name: Build wheels uses: pypa/cibuildwheel@v2.19.2 env: CIBW_ARCHS: ${{ matrix.cibw_archs }} CIBW_SKIP: "cp311-* cp312-* pp*" CIBW_TEST_REQUIRES: pytest CIBW_TEST_COMMAND: > python -X faulthandler {package}/test/debug.py && python -X faulthandler -m pytest -p no:faulthandler -s {package} - uses: actions/upload-artifact@v3 with: path: ./wheelhouse/*.whl build_sdist: name: Build sdist runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build sdist run: pipx run build --sdist - name: Copy sdist package run: cp dist/frozendict-*.tar.gz dist/frozendict.tar.gz - name: Build wheels uses: pypa/cibuildwheel@v2.19.2 env: CIBW_ARCHS: native CIBW_BUILD: "cp310-*" CIBW_SKIP: "*-musllinux_*" CIBW_TEST_REQUIRES: pytest CIBW_TEST_COMMAND: > python -X faulthandler {package}/test/debug.py && python -X faulthandler -m pytest -p no:faulthandler -s {package} with: package-dir: dist/frozendict.tar.gz - name: Remove test sdist package run: rm dist/frozendict.tar.gz - name: Upload sdist artifact uses: actions/upload-artifact@v3 with: path: dist/*.tar.gz python-frozendict/.gitignore000066400000000000000000000004421470250572200165400ustar00rootroot00000000000000# Compiled stuff *.pyc *.pyo doc/_build build *.so # Crap created by version control .orig .rej # Files created by distutils MANIFEST dist *.egg-info .cache # OS X .DS_Store # Other *nix:es .~ # ? index-*.html # Custom venv* Pipfile test/core.* .eggs/ .idea/ .vs/ .coverage coverage/ python-frozendict/LICENSE.txt000066400000000000000000000167441470250572200164070ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. python-frozendict/MANIFEST.in000066400000000000000000000002231470250572200163030ustar00rootroot00000000000000recursive-include src/frozendict/c_src * include test/* include src/frozendict/py.typed include src/frozendict/__init__.pyi include pyproject.toml python-frozendict/README.md000066400000000000000000000532561470250572200160420ustar00rootroot00000000000000# frozendict ### Table of Contents * [Introduction](#introduction) * [Install](#install) * [API](#api) * [frozendict API](#frozendict-api) * [deepfreeze API](#deepfreeze-api) * [Examples](#examples) * [frozendict examples](#frozendict-examples) * [deepfreeze examples](#deepfreeze-examples) * [Building](#building) * [Benchmarks](#benchmarks) # Introduction Welcome, fellow programmer, to the house of `frozendict` and [deepfreeze](#deepfreeze-api)! `frozendict` is a simple immutable dictionary. It's fast as `dict`, and [sometimes faster](https://github.com/Marco-Sulla/python-frozendict#benchmarks)! Unlike other similar implementations, immutability is guaranteed: you can't change the internal variables of the class, and they are all immutable objects. Reinvoking `__init__` does not alter the object. The API is the same as `dict`, without methods that can change the immutability. So it supports also `fromkeys`, unlike other implementations. Furthermore, it can be `pickle`d, un`pickle`d and have a hash, if all values are hashable. You can also add any `dict` to a `frozendict` using the `|` operator. The result is a new `frozendict`. # Install You can install `frozendict` by simply typing in a command line: ```bash pip install frozendict ``` The C Extension is optional by default from version 2.3.5. You can make it mandatory using: ```bash CIBUILDWHEEL=1 pip install frozendict ``` On the contrary, if you want the pure py implementation: ```bash FROZENDICT_PURE_PY=1 pip install frozendict ``` # API ## frozendict API The API is the same of `dict` of Python 3.10, without the methods and operands which alter the map. Additionally, `frozendict` supports these methods: ### `__hash__()` If all the values of the `frozendict` are hashable, returns a hash, otherwise raises a TypeError. ### `set(key, value)` It returns a new `frozendict`. If key is already in the original `frozendict`, the new one will have it with the new value associated. Otherwise, the new `frozendict` will contain the new (key, value) item. ### `delete(key)` It returns a new `frozendict` without the item corresponding to the key. If the key is not present, a KeyError is raised. ### `setdefault(key[, default])` If key is already in `frozendict`, the object itself is returned unchanged. Otherwise, the new `frozendict` will contain the new (key, default) item. The parameter default defaults to None. ### `key([index])` It returns the key at the specified index (determined by the insertion order). If index is not passed, it defaults to 0. If the index is negative, the position will be the size of the `frozendict` + index ### `value([index])` Same as `key(index)`, but it returns the value at the given index. ### `item([index])` Same as `key(index)`, but it returns a tuple with (key, value) at the given index. ## deepfreeze API The `frozendict` _module_ has also these static methods: ### `frozendict.deepfreeze(o, custom_converters = None, custom_inverse_converters = None)` Converts the object and all the objects nested in it, into their immutable counterparts. The conversion map is in `getFreezeConversionMap()`. You can register a new conversion using `register()` You can also pass a map of custom converters with `custom_converters` and a map of custom inverse converters with `custom_inverse_converters`, without using `register()`. By default, if the type is not registered and has a `__dict__` attribute, it's converted to the `frozendict` of that `__dict__`. This function assumes that hashable == immutable (that is not always true). This function uses recursion, with all the limits of recursions in Python. Where is a good old tail call when you need it? ### `frozendict.register(to_convert, converter, *, inverse = False)` Adds a `converter` for a type `to_convert`. `converter` must be callable. The new converter will be used by `deepfreeze()` and has precedence over any previous converter. If `to_covert` has already a converter, a FreezeWarning is raised. If `inverse` is True, the conversion is considered from an immutable type to a mutable one. This make it possible to convert mutable objects nested in the registered immutable one. ### `frozendict.unregister(type, inverse = False)` Unregister a type from custom conversion. If `inverse` is `True`, the unregistered conversion is an inverse conversion (see `register()`). # Examples ## frozendict examples ```python from frozendict import frozendict fd = frozendict(Guzzanti = "Corrado", Hicks = "Bill") print(fd) # frozendict({'Guzzanti': 'Corrado', 'Hicks': 'Bill'}) frozendict({"Guzzanti": "Corrado", "Hicks": "Bill"}) # frozendict({'Guzzanti': 'Corrado', 'Hicks': 'Bill'}) hash(fd) # 5833699487320513741 fd_unhashable = frozendict({1: []}) hash(fd_unhashable) # TypeError: Not all values are hashable. frozendict({frozendict(nested = 4, key = 2): 42}) # frozendict({frozendict({'nested': 4, 'key': 2}): 42}) fd | {1: 2} # frozendict({'Guzzanti': 'Corrado', 'Hicks': 'Bill', 1: 2}) fd.set(1, 2) # frozendict.frozendict({'Guzzanti': 'Corrado', 'Hicks': 'Bill', 1: 2}) fd.set("Guzzanti", "Sabina") # frozendict.frozendict({'Guzzanti': 'Sabina', 'Hicks': 'Bill'}) fd.delete("Guzzanti") # frozendict.frozendict({'Hicks': 'Bill'}) fd.setdefault("Guzzanti", "Sabina") # frozendict.frozendict({'Guzzanti': 'Corrado', 'Hicks': 'Bill'}) fd.setdefault(1, 2) # frozendict.frozendict({'Guzzanti': 'Corrado', 'Hicks': 'Bill', 1: 2}) fd.key() # 'Guzzanti' fd.value(1) # 'Bill' fd.item(-1) # (1, 2) print(fd["Guzzanti"]) # Corrado fd["Brignano"] # KeyError: 'Brignano' len(fd) # 2 "Guzzanti" in fd # True "Guzzanti" not in fd # False "Brignano" in fd # False fd5 = frozendict(fd) id_fd5 = id(fd5) fd5 |= {1: 2} fd5 # frozendict.frozendict({'Guzzanti': 'Corrado', 'Hicks': 'Bill', 1: 2}) id(fd5) != id_fd5 # True fd2 = fd.copy() fd2 == fd # True fd3 = frozendict(fd) fd3 == fd # True fd4 = frozendict({"Hicks": "Bill", "Guzzanti": "Corrado"}) print(fd4) # frozendict({'Hicks': 'Bill', 'Guzzanti': 'Corrado'}) fd4 == fd # True import pickle fd_unpickled = pickle.loads(pickle.dumps(fd)) print(fd_unpickled) # frozendict({'Guzzanti': 'Corrado', 'Hicks': 'Bill'}) fd_unpickled == fd # True frozendict(Guzzanti="Corrado", Hicks="Bill") # frozendict({'Guzzanti': 'Corrado', 'Hicks': 'Bill'} fd.get("Guzzanti") # 'Corrado' print(fd.get("Brignano")) # None tuple(fd.keys()) # ('Guzzanti', 'Hicks') tuple(fd.values()) # ('Corrado', 'Bill') tuple(fd.items()) # (('Guzzanti', 'Corrado'), ('Hicks', 'Bill')) frozendict.fromkeys(["Corrado", "Sabina"], "Guzzanti") # frozendict({'Corrado': 'Guzzanti', 'Sabina': 'Guzzanti'}) iter(fd) # fd["Guzzanti"] = "Caterina" # TypeError: 'frozendict' object doesn't support item assignment ``` ## deepfreeze examples ```python import frozendict as cool from frozendict import frozendict from array import array from collections import OrderedDict from types import MappingProxyType class A: def __init__(self, x): self.x = x a = A(3) o = {"x": [ 5, frozendict(y = {5, "b", memoryview(b"b")}), array("B", (0, 1, 2)), OrderedDict(a=bytearray(b"a")), MappingProxyType({2: []}), a ]} cool.deepfreeze(o) # frozendict(x = ( # 5, # frozendict(y = frozenset({5, "b", memoryview(b"b")})), # (0, 1, 2), # frozendict(a = b'a'), # MappingProxyType({2: ()}), # frozendict(x = 3), # )) ``` # Building You can build `frozendict` directly from the code, using ``` python3 setup.py bdist_wheel ``` The C Extension is optional by default from version 2.3.5. You can make it mandatory by passing the environment variable `CIBUILDWHEEL` with value `1` On the contrary, if you want the pure py implementation, you can pass the env var `FROZENDICT_PURE_PY` with value `1` # Benchmarks Some benchmarks between `dict` and `frozendict`[1]: ``` ################################################################################ //////////////////////////////////////////////////////////////////////////////// Name: `constructor(d)`; Size: 5; Keys: str; Type: dict; Time: 8.02e-08; Sigma: 4e-09 Name: `constructor(d)`; Size: 5; Keys: str; Type: frozendict; Time: 8.81e-08; Sigma: 3e-09 //////////////////////////////////////////////////////////////////////////////// Name: `constructor(d)`; Size: 5; Keys: int; Type: dict; Time: 7.96e-08; Sigma: 5e-09 Name: `constructor(d)`; Size: 5; Keys: int; Type: frozendict; Time: 8.97e-08; Sigma: 2e-09 //////////////////////////////////////////////////////////////////////////////// Name: `constructor(d)`; Size: 1000; Keys: str; Type: dict; Time: 6.38e-06; Sigma: 9e-08 Name: `constructor(d)`; Size: 1000; Keys: str; Type: frozendict; Time: 6.21e-06; Sigma: 2e-07 //////////////////////////////////////////////////////////////////////////////// Name: `constructor(d)`; Size: 1000; Keys: int; Type: dict; Time: 3.49e-06; Sigma: 3e-07 Name: `constructor(d)`; Size: 1000; Keys: int; Type: frozendict; Time: 3.48e-06; Sigma: 2e-07 ################################################################################ //////////////////////////////////////////////////////////////////////////////// Name: `constructor(kwargs)`; Size: 5; Keys: str; Type: dict; Time: 2.40e-07; Sigma: 1e-09 Name: `constructor(kwargs)`; Size: 5; Keys: str; Type: frozendict; Time: 2.48e-07; Sigma: 2e-09 //////////////////////////////////////////////////////////////////////////////// Name: `constructor(kwargs)`; Size: 1000; Keys: str; Type: dict; Time: 4.80e-05; Sigma: 1e-06 Name: `constructor(kwargs)`; Size: 1000; Keys: str; Type: frozendict; Time: 2.90e-05; Sigma: 7e-07 ################################################################################ //////////////////////////////////////////////////////////////////////////////// Name: `constructor(seq2)`; Size: 5; Keys: str; Type: dict; Time: 2.01e-07; Sigma: 9e-10 Name: `constructor(seq2)`; Size: 5; Keys: str; Type: frozendict; Time: 2.50e-07; Sigma: 1e-09 //////////////////////////////////////////////////////////////////////////////// Name: `constructor(seq2)`; Size: 5; Keys: int; Type: dict; Time: 2.18e-07; Sigma: 2e-09 Name: `constructor(seq2)`; Size: 5; Keys: int; Type: frozendict; Time: 2.73e-07; Sigma: 1e-09 //////////////////////////////////////////////////////////////////////////////// Name: `constructor(seq2)`; Size: 1000; Keys: str; Type: dict; Time: 4.29e-05; Sigma: 6e-07 Name: `constructor(seq2)`; Size: 1000; Keys: str; Type: frozendict; Time: 4.33e-05; Sigma: 6e-07 //////////////////////////////////////////////////////////////////////////////// Name: `constructor(seq2)`; Size: 1000; Keys: int; Type: dict; Time: 3.04e-05; Sigma: 4e-07 Name: `constructor(seq2)`; Size: 1000; Keys: int; Type: frozendict; Time: 3.45e-05; Sigma: 4e-07 ################################################################################ //////////////////////////////////////////////////////////////////////////////// Name: `constructor(o)`; Size: 5; Keys: str; Type: dict; Time: 7.93e-08; Sigma: 3e-09 Name: `constructor(o)`; Size: 5; Keys: str; Type: frozendict; Time: 2.41e-08; Sigma: 6e-10 //////////////////////////////////////////////////////////////////////////////// Name: `constructor(o)`; Size: 5; Keys: int; Type: dict; Time: 7.94e-08; Sigma: 5e-09 Name: `constructor(o)`; Size: 5; Keys: int; Type: frozendict; Time: 2.41e-08; Sigma: 6e-10 //////////////////////////////////////////////////////////////////////////////// Name: `constructor(o)`; Size: 1000; Keys: str; Type: dict; Time: 6.18e-06; Sigma: 3e-07 Name: `constructor(o)`; Size: 1000; Keys: str; Type: frozendict; Time: 2.41e-08; Sigma: 6e-10 //////////////////////////////////////////////////////////////////////////////// Name: `constructor(o)`; Size: 1000; Keys: int; Type: dict; Time: 3.47e-06; Sigma: 2e-07 Name: `constructor(o)`; Size: 1000; Keys: int; Type: frozendict; Time: 2.41e-08; Sigma: 6e-10 ################################################################################ //////////////////////////////////////////////////////////////////////////////// Name: `o.copy()`; Size: 5; Keys: str; Type: dict; Time: 7.28e-08; Sigma: 2e-09 Name: `o.copy()`; Size: 5; Keys: str; Type: frozendict; Time: 3.18e-08; Sigma: 2e-09 //////////////////////////////////////////////////////////////////////////////// Name: `o.copy()`; Size: 5; Keys: int; Type: dict; Time: 7.21e-08; Sigma: 4e-09 Name: `o.copy()`; Size: 5; Keys: int; Type: frozendict; Time: 3.32e-08; Sigma: 2e-09 //////////////////////////////////////////////////////////////////////////////// Name: `o.copy()`; Size: 1000; Keys: str; Type: dict; Time: 6.16e-06; Sigma: 3e-07 Name: `o.copy()`; Size: 1000; Keys: str; Type: frozendict; Time: 3.18e-08; Sigma: 2e-09 //////////////////////////////////////////////////////////////////////////////// Name: `o.copy()`; Size: 1000; Keys: int; Type: dict; Time: 3.46e-06; Sigma: 1e-07 Name: `o.copy()`; Size: 1000; Keys: int; Type: frozendict; Time: 3.18e-08; Sigma: 2e-09 ################################################################################ //////////////////////////////////////////////////////////////////////////////// Name: `o == o`; Size: 5; Keys: str; Type: dict; Time: 7.23e-08; Sigma: 8e-10 Name: `o == o`; Size: 5; Keys: str; Type: frozendict; Time: 2.44e-08; Sigma: 2e-09 //////////////////////////////////////////////////////////////////////////////// Name: `o == o`; Size: 5; Keys: int; Type: dict; Time: 7.30e-08; Sigma: 1e-09 Name: `o == o`; Size: 5; Keys: int; Type: frozendict; Time: 2.44e-08; Sigma: 2e-09 //////////////////////////////////////////////////////////////////////////////// Name: `o == o`; Size: 1000; Keys: str; Type: dict; Time: 1.38e-05; Sigma: 1e-07 Name: `o == o`; Size: 1000; Keys: str; Type: frozendict; Time: 2.44e-08; Sigma: 2e-09 //////////////////////////////////////////////////////////////////////////////// Name: `o == o`; Size: 1000; Keys: int; Type: dict; Time: 1.05e-05; Sigma: 7e-08 Name: `o == o`; Size: 1000; Keys: int; Type: frozendict; Time: 2.44e-08; Sigma: 2e-09 ################################################################################ //////////////////////////////////////////////////////////////////////////////// Name: `for x in o`; Size: 5; Keys: str; Type: dict; Time: 7.33e-08; Sigma: 2e-09 Name: `for x in o`; Size: 5; Keys: str; Type: frozendict; Time: 6.70e-08; Sigma: 1e-09 //////////////////////////////////////////////////////////////////////////////// Name: `for x in o`; Size: 5; Keys: int; Type: dict; Time: 7.33e-08; Sigma: 2e-09 Name: `for x in o`; Size: 5; Keys: int; Type: frozendict; Time: 6.70e-08; Sigma: 1e-09 //////////////////////////////////////////////////////////////////////////////// Name: `for x in o`; Size: 1000; Keys: str; Type: dict; Time: 8.84e-06; Sigma: 5e-08 Name: `for x in o`; Size: 1000; Keys: str; Type: frozendict; Time: 7.06e-06; Sigma: 6e-08 //////////////////////////////////////////////////////////////////////////////// Name: `for x in o`; Size: 1000; Keys: int; Type: dict; Time: 8.67e-06; Sigma: 7e-08 Name: `for x in o`; Size: 1000; Keys: int; Type: frozendict; Time: 6.94e-06; Sigma: 3e-08 ################################################################################ //////////////////////////////////////////////////////////////////////////////// Name: `for x in o.values()`; Size: 5; Keys: str; Type: dict; Time: 7.28e-08; Sigma: 9e-10 Name: `for x in o.values()`; Size: 5; Keys: str; Type: frozendict; Time: 6.48e-08; Sigma: 8e-10 //////////////////////////////////////////////////////////////////////////////// Name: `for x in o.values()`; Size: 5; Keys: int; Type: dict; Time: 7.25e-08; Sigma: 1e-09 Name: `for x in o.values()`; Size: 5; Keys: int; Type: frozendict; Time: 6.45e-08; Sigma: 1e-09 //////////////////////////////////////////////////////////////////////////////// Name: `for x in o.values()`; Size: 1000; Keys: str; Type: dict; Time: 9.06e-06; Sigma: 5e-07 Name: `for x in o.values()`; Size: 1000; Keys: str; Type: frozendict; Time: 7.04e-06; Sigma: 4e-08 //////////////////////////////////////////////////////////////////////////////// Name: `for x in o.values()`; Size: 1000; Keys: int; Type: dict; Time: 9.53e-06; Sigma: 3e-08 Name: `for x in o.values()`; Size: 1000; Keys: int; Type: frozendict; Time: 6.97e-06; Sigma: 3e-08 ################################################################################ //////////////////////////////////////////////////////////////////////////////// Name: `for x in o.items()`; Size: 5; Keys: str; Type: dict; Time: 1.13e-07; Sigma: 3e-09 Name: `for x in o.items()`; Size: 5; Keys: str; Type: frozendict; Time: 1.16e-07; Sigma: 2e-09 //////////////////////////////////////////////////////////////////////////////// Name: `for x in o.items()`; Size: 5; Keys: int; Type: dict; Time: 1.14e-07; Sigma: 3e-09 Name: `for x in o.items()`; Size: 5; Keys: int; Type: frozendict; Time: 1.17e-07; Sigma: 2e-09 //////////////////////////////////////////////////////////////////////////////// Name: `for x in o.items()`; Size: 1000; Keys: str; Type: dict; Time: 1.53e-05; Sigma: 3e-07 Name: `for x in o.items()`; Size: 1000; Keys: str; Type: frozendict; Time: 1.53e-05; Sigma: 4e-07 //////////////////////////////////////////////////////////////////////////////// Name: `for x in o.items()`; Size: 1000; Keys: int; Type: dict; Time: 1.53e-05; Sigma: 3e-07 Name: `for x in o.items()`; Size: 1000; Keys: int; Type: frozendict; Time: 1.55e-05; Sigma: 4e-07 ################################################################################ //////////////////////////////////////////////////////////////////////////////// Name: `pickle.dumps(o)`; Size: 5; Keys: str; Type: dict; Time: 6.82e-07; Sigma: 2e-08 Name: `pickle.dumps(o)`; Size: 5; Keys: str; Type: frozendict; Time: 2.86e-06; Sigma: 1e-07 //////////////////////////////////////////////////////////////////////////////// Name: `pickle.dumps(o)`; Size: 5; Keys: int; Type: dict; Time: 4.77e-07; Sigma: 2e-08 Name: `pickle.dumps(o)`; Size: 5; Keys: int; Type: frozendict; Time: 2.72e-06; Sigma: 8e-08 //////////////////////////////////////////////////////////////////////////////// Name: `pickle.dumps(o)`; Size: 1000; Keys: str; Type: dict; Time: 1.24e-04; Sigma: 4e-06 Name: `pickle.dumps(o)`; Size: 1000; Keys: str; Type: frozendict; Time: 1.92e-04; Sigma: 5e-06 //////////////////////////////////////////////////////////////////////////////// Name: `pickle.dumps(o)`; Size: 1000; Keys: int; Type: dict; Time: 2.81e-05; Sigma: 6e-07 Name: `pickle.dumps(o)`; Size: 1000; Keys: int; Type: frozendict; Time: 7.37e-05; Sigma: 1e-06 ################################################################################ //////////////////////////////////////////////////////////////////////////////// Name: `pickle.loads(dump)`; Size: 5; Keys: str; Type: dict; Time: 9.08e-07; Sigma: 6e-09 Name: `pickle.loads(dump)`; Size: 5; Keys: str; Type: frozendict; Time: 1.79e-06; Sigma: 9e-08 //////////////////////////////////////////////////////////////////////////////// Name: `pickle.loads(dump)`; Size: 5; Keys: int; Type: dict; Time: 4.46e-07; Sigma: 6e-09 Name: `pickle.loads(dump)`; Size: 5; Keys: int; Type: frozendict; Time: 1.32e-06; Sigma: 7e-08 //////////////////////////////////////////////////////////////////////////////// Name: `pickle.loads(dump)`; Size: 1000; Keys: str; Type: dict; Time: 1.57e-04; Sigma: 8e-06 Name: `pickle.loads(dump)`; Size: 1000; Keys: str; Type: frozendict; Time: 1.69e-04; Sigma: 7e-06 //////////////////////////////////////////////////////////////////////////////// Name: `pickle.loads(dump)`; Size: 1000; Keys: int; Type: dict; Time: 5.97e-05; Sigma: 5e-06 Name: `pickle.loads(dump)`; Size: 1000; Keys: int; Type: frozendict; Time: 6.68e-05; Sigma: 2e-06 ################################################################################ //////////////////////////////////////////////////////////////////////////////// Name: `class.fromkeys()`; Size: 5; Keys: str; Type: dict; Time: 1.88e-07; Sigma: 1e-09 Name: `class.fromkeys()`; Size: 5; Keys: str; Type: frozendict; Time: 2.22e-07; Sigma: 7e-09 //////////////////////////////////////////////////////////////////////////////// Name: `class.fromkeys()`; Size: 5; Keys: int; Type: dict; Time: 2.08e-07; Sigma: 6e-09 Name: `class.fromkeys()`; Size: 5; Keys: int; Type: frozendict; Time: 2.44e-07; Sigma: 2e-09 //////////////////////////////////////////////////////////////////////////////// Name: `class.fromkeys()`; Size: 1000; Keys: str; Type: dict; Time: 4.05e-05; Sigma: 4e-06 Name: `class.fromkeys()`; Size: 1000; Keys: str; Type: frozendict; Time: 3.84e-05; Sigma: 5e-07 //////////////////////////////////////////////////////////////////////////////// Name: `class.fromkeys()`; Size: 1000; Keys: int; Type: dict; Time: 2.93e-05; Sigma: 7e-07 Name: `class.fromkeys()`; Size: 1000; Keys: int; Type: frozendict; Time: 3.08e-05; Sigma: 2e-06 ################################################################################ ``` [1] Benchmarks done under Linux 64 bit, Python 3.10.2, using the C Extension. python-frozendict/build.bat000066400000000000000000000001241470250572200163340ustar00rootroot00000000000000del /S /Q build dist frozendict.egg-info set CIBUILDWHEEL="1" python setup.py %* python-frozendict/build.sh000077500000000000000000000000721470250572200162050ustar00rootroot00000000000000rm -rf build dist *.egg-info CIBUILDWHEEL=1 ./setup.py $* python-frozendict/build_py.sh000077500000000000000000000001001470250572200167050ustar00rootroot00000000000000rm -rf build dist *.egg-info FROZENDICT_PURE_PY=1 ./setup.py $* python-frozendict/mytest.bat000066400000000000000000000000401470250572200165570ustar00rootroot00000000000000python test/debug.py && pytest python-frozendict/mytest.sh000077500000000000000000000000521470250572200164310ustar00rootroot00000000000000rm test/core.* ./test/debug.py && pytest python-frozendict/pyproject.toml000066400000000000000000000000671470250572200174670ustar00rootroot00000000000000[tool.cibuildwheel] skip=["cp311-*", "cp312-*", "pp*"] python-frozendict/setup.py000077500000000000000000000141711470250572200162710ustar00rootroot00000000000000#!/usr/bin/env python3 import setuptools from pathlib import Path import sys from platform import python_implementation from os import environ name = "frozendict" module1_name = "frozendict" readme_filename = "README.md" version_filename = "version.py" py_typed_filename = "py.typed" mypy_filename = "__init__.pyi" main_url = "https://github.com/Marco-Sulla/python-frozendict" bug_url = "https://github.com/Marco-Sulla/python-frozendict/issues" author = "Marco Sulla" author_email = "marcosullaroma@gmail.com" mylicense = "LGPL v3" license_files = "LICENSE.txt" description = "A simple immutable dictionary" keywords = ( "immutable hashable picklable frozendict dict dictionary " + "map Mapping MappingProxyType developers stable utility" ) python_requires = ">=3.6" classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.6", "Natural Language :: English", "Operating System :: OS Independent", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Utilities", ] curr_path = Path(__file__).resolve() curr_dir = curr_path.parent readme_path = curr_dir / readme_filename readme_content_type = "text/markdown" with open(readme_path) as f: long_description = f.read() package_dir_name = "src" package_path = curr_dir / package_dir_name module1_dir_name = module1_name module1_path = package_path / module1_dir_name version_path = module1_path / version_filename with open(version_path) as f: # create the version var exec(f.read()) package_path_str = str(package_path) packages = setuptools.find_packages(where = package_path_str) package_data_filenames = (py_typed_filename, mypy_filename) package_data = { package_name: package_data_filenames for package_name in packages } # C extension - START c_src_dir_name = "c_src" c_src_base_path = module1_path / c_src_dir_name include_dir_name = "Include" ext1_name = "_" + name ext1_fullname = module1_name + "." + ext1_name ext1_source1_name = name + "object" ext1_source1_fullname = ext1_source1_name + ".c" cpython_objects_dir_name = "Objects" cpython_stringlib_name = "stringlib" cpython_objects_clinic_name = "clinic" extra_compile_args = ["-DPY_SSIZE_T_CLEAN", ] pyversion = sys.version_info cpython_version = f"{pyversion[0]}_{pyversion[1]}" c_src_path = c_src_base_path / cpython_version cpython_path = c_src_path / "cpython_src" cpython_object_path = cpython_path / cpython_objects_dir_name include_path = c_src_path / include_dir_name cpython_stringlib_path = cpython_object_path / cpython_stringlib_name cpython_objects_clinic_path = cpython_object_path / cpython_objects_clinic_name cpython_include_dirs = [ str(include_path), str(cpython_object_path), str(cpython_stringlib_path), str(cpython_objects_clinic_path), str(cpython_path), ] ext1_source1_path = c_src_path / ext1_source1_fullname cpython_sources_tmp = [ext1_source1_path, ] cpython_sources = [ str(x.relative_to(curr_dir)) for x in cpython_sources_tmp ] undef_macros = [] argv = sys.argv argv_1_exists = len(argv) > 1 if argv_1_exists and argv[1] == "c_debug": undef_macros = ["NDEBUG"] # noinspection PyShadowingNames def get_ext_module( fullname, sources, include_dirs, extra_compile_args, undef_macros, optional ): ext_module = setuptools.Extension( fullname, sources = sources, include_dirs = include_dirs, extra_compile_args = extra_compile_args, undef_macros = undef_macros, optional = optional, ) return ext_module # noinspection PyShadowingNames def get_ext_module_1(optional): return get_ext_module( fullname = ext1_fullname, sources = cpython_sources, include_dirs = cpython_include_dirs, extra_compile_args = extra_compile_args, undef_macros = undef_macros, optional = optional, ) # C extension - END # noinspection PyUnresolvedReferences common_setup_args = dict( name = name, author = author, author_email = author_email, version = version, python_requires = python_requires, license = mylicense, license_files = (license_files, ), url = main_url, project_urls = { "Bug Reports": bug_url, "Source": main_url, }, packages = packages, package_dir = {"": package_dir_name}, package_data = package_data, description = description, long_description = long_description, long_description_content_type = readme_content_type, classifiers = classifiers, keywords = keywords, ) custom_arg = None custom_args_py = ("py", ) custom_args_c = ("c", "c_debug") custom_args = custom_args_py + custom_args_c if argv_1_exists and argv[1] in custom_args: custom_arg = argv[1] sys.argv = [argv[0]] + argv[2:] impl = python_implementation() # C Extension is optional by default from version 2.3.5 # If the module is built by pipeline, C Extension must be mandatory. optional = environ.get('CIBUILDWHEEL') != '1' # Check if build the pure py implementation pure_py_env = environ.get('FROZENDICT_PURE_PY') pure_py = pure_py_env == '1' mix_c_py_error = ValueError( "You can't specify the pure py implementation *and* C " + "extension togheter" ) if custom_arg is None: if impl == "PyPy": custom_arg = "py" else: custom_arg = "py" if pure_py else "c" elif custom_arg in custom_args_c: if pure_py: raise mix_c_py_error optional = False if pure_py and not optional: raise mix_c_py_error if custom_arg in custom_args_py: # check if pure py explicitly disabled if pure_py_env == '0': raise mix_c_py_error setuptools.setup(**common_setup_args) elif custom_arg in custom_args_c: ext_module_1 = get_ext_module_1(optional) ext_modules = [ext_module_1] setuptools.setup(ext_modules = ext_modules, **common_setup_args) else: raise ValueError(f"Unsupported custom_arg {custom_arg}") python-frozendict/src/000077500000000000000000000000001470250572200153375ustar00rootroot00000000000000python-frozendict/src/frozendict/000077500000000000000000000000001470250572200175065ustar00rootroot00000000000000python-frozendict/src/frozendict/__init__.py000066400000000000000000000027751470250572200216320ustar00rootroot00000000000000r""" Provides frozendict, a simple immutable dictionary. """ try: # pragma: no cover from ._frozendict import * c_ext = True # noinspection PyUnresolvedReferences del _frozendict except ImportError: from ._frozendict_py import * c_ext = False from .version import version as __version__ from . import monkeypatch from .cool import * from . import cool def _getFrozendictJsonEncoder(BaseJsonEncoder = None): if BaseJsonEncoder is None: # pragma: no cover from json.encoder import JSONEncoder BaseJsonEncoder = JSONEncoder class FrozendictJsonEncoderInternal(BaseJsonEncoder): def default(self, obj): if isinstance(obj, frozendict): # pragma: no cover # TODO create a C serializer return dict(obj) return BaseJsonEncoder.default( self, obj ) # pragma: no cover return FrozendictJsonEncoderInternal FrozendictJsonEncoder = _getFrozendictJsonEncoder() monkeypatch.patchOrUnpatchAll(patch = True, warn = False) from collections.abc import Mapping # noinspection PyUnresolvedReferences Mapping.register(frozendict) del Mapping if c_ext: # pragma: no cover __all__ = (frozendict.__name__, ) else: __all__ = _frozendict_py.__all__ del _frozendict_py # TODO deprecated, to remove in future versions FrozenOrderedDict = frozendict __all__ += cool.__all__ __all__ += (FrozendictJsonEncoder.__name__, "FrozenOrderedDict") python-frozendict/src/frozendict/__init__.pyi000066400000000000000000000072441470250572200217770ustar00rootroot00000000000000from __future__ import annotations from typing import ( TypeVar, overload, Optional, Union, Any, Dict, Callable, ) from collections.abc import Hashable try: from typing import Mapping, Iterable, Iterator, Tuple, Type except ImportError: from collections.abc import Mapping, Iterable, Iterator Tuple = tuple Type = type K = TypeVar("K") V = TypeVar("V", covariant=True) K2 = TypeVar("K2") V2 = TypeVar("V2", covariant=True) SelfT = TypeVar("SelfT", bound=frozendict[K, V]) # noinspection PyPep8Naming class frozendict(Mapping[K, V]): @overload def __new__(cls: Type[SelfT]) -> SelfT: ... @overload def __new__(cls: Type[SelfT], **kwargs: V) -> frozendict[str, V]: ... @overload def __new__(cls: Type[SelfT], mapping: Mapping[K, V]) -> SelfT: ... @overload def __new__(cls: Type[SelfT], iterable: Iterable[Tuple[K, V]]) -> SelfT: ... def __getitem__(self: SelfT, key: K) -> V: ... def __len__(self: SelfT) -> int: ... def __iter__(self: SelfT) -> Iterator[K]: ... def __hash__(self: SelfT) -> int: ... def __reversed__(self: SelfT) -> Iterator[K]: ... def copy(self: SelfT) -> SelfT: ... def __copy__(self: SelfT) -> SelfT: ... def __deepcopy__(self: SelfT) -> SelfT: ... def delete(self: SelfT, key: K) -> SelfT: ... @overload def key(self: SelfT, index: int) -> K: ... @overload def key(self: SelfT) -> K: ... @overload def value(self: SelfT, index: int) -> V: ... @overload def value(self: SelfT) -> V: ... @overload def item(self: SelfT, index: int) -> Tuple[K, V]: ... @overload def item(self: SelfT) -> Tuple[K, V]: ... @overload def __or__(self: SelfT, other: Mapping[K, V]) -> SelfT: ... @overload def __or__(self: SelfT, other: Mapping[K2, V]) -> frozendict[Union[K, K2], V]: ... @overload def __or__(self: SelfT, other: Mapping[K, V2]) -> frozendict[K, Union[V, V2]]: ... @overload def __or__(self: SelfT, other: Mapping[K2, V2]) -> frozendict[Union[K, K2], Union[V, V2]]: ... @overload def set(self: SelfT, key: K, value: V) -> SelfT: ... @overload def set(self: SelfT, key: K2, value: V) -> frozendict[Union[K, K2], V]: ... @overload def set(self: SelfT, key: K, value: V2) -> frozendict[K, Union[V, V2]]: ... @overload def set(self: SelfT, key: K2, value: V2) -> frozendict[Union[K, K2], Union[V, V2]]: ... @overload def setdefault(self: SelfT, key: K) -> SelfT: ... @overload def setdefault(self: SelfT, key: K2) -> SelfT: ... @overload def setdefault(self: SelfT, key: K, default: V) -> SelfT: ... @overload def setdefault(self: SelfT, key: K2, default: V) -> frozendict[Union[K, K2], V]: ... @overload def setdefault(self: SelfT, key: K, default: V2) -> frozendict[K, Union[V, V2]]: ... @overload def setdefault(self: SelfT, key: K2, default: V2) -> frozendict[Union[K, K2], Union[V, V2]]: ... @classmethod def fromkeys( cls: Type[SelfT], seq: Iterable[K], value: Optional[V] = None ) -> SelfT: ... FrozenOrderedDict = frozendict c_ext: bool class FreezeError(Exception): pass class FreezeWarning(UserWarning): pass # PyCharm complains about returning Hashable, because # it's not subscriptable def deepfreeze( o: Any, custom_converters: Optional[Dict[Any, Callable[[Any], Hashable]]] = None, custom_inverse_converters: Optional[Dict[Any, Callable[[Any], Any]]] = None ) -> Any: ... def register( to_convert: Any, converter: Callable[[Any], Any], *, inverse: bool = False ) -> None: ... def unregister( type: Any, inverse: bool = False ) -> None: ... python-frozendict/src/frozendict/_frozendict_py.py000066400000000000000000000145561470250572200231110ustar00rootroot00000000000000from copy import deepcopy def immutable(self, *_args, **_kwargs): r""" Function for not implemented method since the object is immutable """ raise AttributeError( f"'{self.__class__.__name__}' object is read-only" ) _empty_frozendict = None _module_name = "frozendict" # noinspection PyPep8Naming class frozendict(dict): r""" A simple immutable dictionary. The API is the same as `dict`, without methods that can change the immutability. In addition, it supports __hash__(). """ __slots__ = ( "_hash", ) @classmethod def fromkeys(cls, *args, **kwargs): r""" Identical to dict.fromkeys(). """ return cls(dict.fromkeys(*args, **kwargs)) # noinspection PyMethodParameters def __new__(e4b37cdf_d78a_4632_bade_6f0579d8efac, *args, **kwargs): cls = e4b37cdf_d78a_4632_bade_6f0579d8efac has_kwargs = bool(kwargs) continue_creation = True self = None # check if there's only an argument and it's of the same class if len(args) == 1 and not has_kwargs: it = args[0] # no isinstance, to avoid subclassing problems if it.__class__ == frozendict and cls == frozendict: self = it continue_creation = False if continue_creation: self = dict.__new__(cls, *args, **kwargs) dict.__init__(self, *args, **kwargs) # empty singleton - start if self.__class__ == frozendict and not len(self): global _empty_frozendict if _empty_frozendict is None: _empty_frozendict = self else: self = _empty_frozendict continue_creation = False # empty singleton - end if continue_creation: object.__setattr__(self, "_hash", -1) return self # noinspection PyMissingConstructor def __init__(self, *args, **kwargs): pass def __hash__(self, *args, **kwargs): r""" Calculates the hash if all values are hashable, otherwise raises a TypeError. """ if self._hash != -1: _hash = self._hash else: fs = frozenset(self.items()) _hash = hash(fs) object.__setattr__(self, "_hash", _hash) return _hash def __repr__(self, *args, **kwargs): r""" Identical to dict.__repr__(). """ body = super().__repr__(*args, **kwargs) klass = self.__class__ if klass == frozendict: name = f"{_module_name}.{klass.__name__}" else: name = klass.__name__ return f"{name}({body})" def copy(self): r""" Return the object itself, as it's an immutable. """ klass = self.__class__ if klass == frozendict: return self return klass(self) def __copy__(self, *args, **kwargs): r""" See copy(). """ return self.copy() def __deepcopy__(self, memo, *args, **kwargs): r""" As for tuples, if hashable, see copy(); otherwise, it returns a deepcopy. """ klass = self.__class__ return_copy = klass == frozendict if return_copy: try: hash(self) except TypeError: return_copy = False if return_copy: return self.copy() tmp = deepcopy(dict(self)) return klass(tmp) def __reduce__(self, *args, **kwargs): r""" Support for `pickle`. """ return (self.__class__, (dict(self),)) def set(self, key, val): new_self = dict(self) new_self[key] = val return self.__class__(new_self) def setdefault(self, key, default=None): if key in self: return self new_self = dict(self) new_self[key] = default return self.__class__(new_self) def delete(self, key): new_self = dict(self) del new_self[key] if new_self: return self.__class__(new_self) return self.__class__() def _get_by_index(self, collection, index): try: return collection[index] except IndexError: maxindex = len(collection) - 1 name = self.__class__.__name__ raise IndexError( f"{name} index {index} out of range {maxindex}" ) from None def key(self, index=0): collection = tuple(self.keys()) return self._get_by_index(collection, index) def value(self, index=0): collection = tuple(self.values()) return self._get_by_index(collection, index) def item(self, index=0): collection = tuple(self.items()) return self._get_by_index(collection, index) def __setitem__(self, key, val, *args, **kwargs): raise TypeError( f"'{self.__class__.__name__}' object doesn't support item " "assignment" ) def __delitem__(self, key, *args, **kwargs): raise TypeError( f"'{self.__class__.__name__}' object doesn't support item " "deletion" ) def frozendict_or(self, other, *_args, **_kwargs): res = {} res.update(self) res.update(other) return self.__class__(res) frozendict.__or__ = frozendict_or frozendict.__ior__ = frozendict_or try: # noinspection PyStatementEffect frozendict.__reversed__ except AttributeError: # pragma: no cover def frozendict_reversed(self, *_args, **_kwargs): return reversed(tuple(self)) frozendict.__reversed__ = frozendict_reversed frozendict.clear = immutable frozendict.pop = immutable frozendict.popitem = immutable frozendict.update = immutable frozendict.__delattr__ = immutable frozendict.__setattr__ = immutable frozendict.__module__ = _module_name __all__ = (frozendict.__name__,) python-frozendict/src/frozendict/c_src/000077500000000000000000000000001470250572200205775ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_10/000077500000000000000000000000001470250572200212415ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_10/Include/000077500000000000000000000000001470250572200226245ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_10/Include/cpython/000077500000000000000000000000001470250572200243105ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_10/Include/cpython/frozendictobject.h000066400000000000000000000004601470250572200300170ustar00rootroot00000000000000#ifndef Py_CPYTHON_FROZENDICTOBJECT_H # error "this header file must not be included directly" #endif typedef struct { PyObject_HEAD Py_ssize_t ma_used; uint64_t ma_version_tag; PyDictKeysObject* ma_keys; PyObject** ma_values; Py_hash_t ma_hash; } PyFrozenDictObject; python-frozendict/src/frozendict/c_src/3_10/Include/frozendictobject.h000066400000000000000000000050501470250572200263330ustar00rootroot00000000000000#ifndef Py_FROZENDICTOBJECT_H #define Py_FROZENDICTOBJECT_H #ifdef __cplusplus extern "C" { #endif // PyAPI_DATA(PyTypeObject) PyFrozenDict_Type; static PyTypeObject PyFrozenDict_Type; // PyAPI_DATA(PyTypeObject) PyCoold_Type; static PyTypeObject PyCoold_Type; #define PyFrozenDict_Check(op) \ ( \ Py_IS_TYPE(op, &PyFrozenDict_Type) \ || PyType_IsSubtype(Py_TYPE(op), &PyFrozenDict_Type) \ ) #define PyFrozenDict_CheckExact(op) Py_IS_TYPE(op, &PyFrozenDict_Type) #define PyAnyFrozenDict_Check(op) \ ( \ Py_IS_TYPE(op, &PyFrozenDict_Type) \ || Py_IS_TYPE(op, &PyCoold_Type) \ || PyType_IsSubtype(Py_TYPE(op), &PyFrozenDict_Type) \ || PyType_IsSubtype(Py_TYPE(op), &PyCoold_Type) \ ) #define PyAnyFrozenDict_CheckExact(op) \ ( \ Py_IS_TYPE(op, &PyFrozenDict_Type) \ || Py_IS_TYPE(op, &PyCoold_Type) \ ) #define PyAnyDict_Check(ob) \ ( \ PyDict_Check(ob) \ || Py_IS_TYPE(ob, &PyFrozenDict_Type) \ || Py_IS_TYPE(ob, &PyCoold_Type) \ || PyType_IsSubtype(Py_TYPE(ob), &PyFrozenDict_Type) \ || PyType_IsSubtype(Py_TYPE(ob), &PyCoold_Type) \ ) #define PyAnyDict_CheckExact(op) ( \ (Py_IS_TYPE(op, &PyDict_Type)) \ || (Py_IS_TYPE(op, &PyFrozenDict_Type)) \ || (Py_IS_TYPE(op, &PyCoold_Type)) \ ) // PyAPI_DATA(PyTypeObject) PyFrozenDictIterKey_Type; static PyTypeObject PyFrozenDictIterKey_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictIterValues_Type; static PyTypeObject PyFrozenDictIterValue_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictIterItem_Type; static PyTypeObject PyFrozenDictIterItem_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictKeys_Type; static PyTypeObject PyFrozenDictKeys_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictValues_Type; static PyTypeObject PyFrozenDictValues_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictItems_Type; static PyTypeObject PyFrozenDictItems_Type; #define PyAnyDictKeys_Check(op) (PyDictKeys_Check(op) || PyObject_TypeCheck(op, &PyFrozenDictKeys_Type)) #define PyAnyDictValues_Check(op) (PyDictValues_Check(op) || PyObject_TypeCheck(op, &PyFrozenDictValues_Type)) #define PyAnyDictItems_Check(op) (PyDictItems_Check(op) || PyObject_TypeCheck(op, &PyFrozenDictItems_Type)) /* This excludes Values, since they are not sets. */ # define PyAnyDictViewSet_Check(op) \ (PyAnyDictKeys_Check(op) || PyAnyDictItems_Check(op)) #ifndef Py_LIMITED_API # define Py_CPYTHON_FROZENDICTOBJECT_H # include "cpython/frozendictobject.h" # undef Py_CPYTHON_FROZENDICTOBJECT_H #endif #ifdef __cplusplus } #endif #endif python-frozendict/src/frozendict/c_src/3_10/cpython_src/000077500000000000000000000000001470250572200235745ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_10/cpython_src/Objects/000077500000000000000000000000001470250572200251655ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_10/cpython_src/Objects/clinic/000077500000000000000000000000001470250572200264265ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_10/cpython_src/Objects/clinic/dictobject.c.h000066400000000000000000000051111470250572200311300ustar00rootroot00000000000000/*[clinic input] preserve [clinic start generated code]*/ PyDoc_STRVAR(dict_fromkeys__doc__, "fromkeys($type, iterable, value=None, /)\n" "--\n" "\n" "Create a new dictionary with keys from iterable and values set to value."); #define DICT_FROMKEYS_METHODDEF \ {"fromkeys", (PyCFunction)(void(*)(void))dict_fromkeys, METH_FASTCALL|METH_CLASS, dict_fromkeys__doc__}, static PyObject * frozendict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value); static PyObject * dict_fromkeys(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; PyObject *iterable; PyObject *value = Py_None; if (!_PyArg_CheckPositional("fromkeys", nargs, 1, 2)) { goto exit; } iterable = args[0]; if (nargs < 2) { goto skip_optional; } value = args[1]; skip_optional: return_value = frozendict_fromkeys_impl(type, iterable, value); exit: return return_value; } PyDoc_STRVAR(dict___contains____doc__, "__contains__($self, key, /)\n" "--\n" "\n" "True if the dictionary has the specified key, else False."); #define DICT___CONTAINS___METHODDEF \ {"__contains__", (PyCFunction)dict___contains__, METH_O|METH_COEXIST, dict___contains____doc__}, PyDoc_STRVAR(dict_get__doc__, "get($self, key, default=None, /)\n" "--\n" "\n" "Return the value for key if key is in the dictionary, else default."); #define DICT_GET_METHODDEF \ {"get", (PyCFunction)(void(*)(void))dict_get, METH_FASTCALL, dict_get__doc__}, static PyObject * dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value); static PyObject * dict_get(PyDictObject *self, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; PyObject *key; PyObject *default_value = Py_None; if (!_PyArg_CheckPositional("get", nargs, 1, 2)) { goto exit; } key = args[0]; if (nargs < 2) { goto skip_optional; } default_value = args[1]; skip_optional: return_value = dict_get_impl(self, key, default_value); exit: return return_value; } PyDoc_STRVAR(dict___reversed____doc__, "__reversed__($self, /)\n" "--\n" "\n" "Return a reverse iterator over the dict keys."); #define DICT___REVERSED___METHODDEF \ {"__reversed__", (PyCFunction)dict___reversed__, METH_NOARGS, dict___reversed____doc__}, static PyObject * dict___reversed___impl(PyDictObject *self); static PyObject * dict___reversed__(PyDictObject *self, PyObject *Py_UNUSED(ignored)) { return dict___reversed___impl(self); } /*[clinic end generated code: output=7b77c16e43d6735a input=a9049054013a1b77]*/ python-frozendict/src/frozendict/c_src/3_10/cpython_src/Objects/dict-common.h000066400000000000000000000043361470250572200275550ustar00rootroot00000000000000#ifndef Py_DICT_COMMON_H #define Py_DICT_COMMON_H typedef struct { /* Cached hash code of me_key. */ Py_hash_t me_hash; PyObject *me_key; PyObject *me_value; /* This field is only meaningful for combined tables */ } PyDictKeyEntry; /* dict_lookup_func() returns index of entry which can be used like DK_ENTRIES(dk)[index]. * -1 when no entry found, -3 when compare raises error. */ typedef Py_ssize_t (*dict_lookup_func) (PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); #define DKIX_EMPTY (-1) #define DKIX_DUMMY (-2) /* Used internally */ #define DKIX_ERROR (-3) /* See dictobject.c for actual layout of DictKeysObject */ struct _dictkeysobject { Py_ssize_t dk_refcnt; /* Size of the hash table (dk_indices). It must be a power of 2. */ Py_ssize_t dk_size; /* Function to lookup in the hash table (dk_indices): - lookdict(): general-purpose, and may return DKIX_ERROR if (and only if) a comparison raises an exception. - lookdict_unicode(): specialized to Unicode string keys, comparison of which can never raise an exception; that function can never return DKIX_ERROR. - lookdict_unicode_nodummy(): similar to lookdict_unicode() but further specialized for Unicode string keys that cannot be the value. - lookdict_split(): Version of lookdict() for split tables. */ dict_lookup_func dk_lookup; /* Number of usable entries in dk_entries. */ Py_ssize_t dk_usable; /* Number of used entries in dk_entries. */ Py_ssize_t dk_nentries; /* Actual hash table of dk_size entries. It holds indices in dk_entries, or DKIX_EMPTY(-1) or DKIX_DUMMY(-2). Indices must be: 0 <= indice < USABLE_FRACTION(dk_size). The size in bytes of an indice depends on dk_size: - 1 byte if dk_size <= 0xff (char*) - 2 bytes if dk_size <= 0xffff (int16_t*) - 4 bytes if dk_size <= 0xffffffff (int32_t*) - 8 bytes otherwise (int64_t*) Dynamically sized, SIZEOF_VOID_P is minimum. */ char dk_indices[]; /* char is required to avoid strict aliasing. */ /* "PyDictKeyEntry dk_entries[dk_usable];" array follows: see the DK_ENTRIES() macro */ }; #endif python-frozendict/src/frozendict/c_src/3_10/cpython_src/Objects/dictobject.c000066400000000000000000001576221470250572200274600ustar00rootroot00000000000000/* Dictionary object implementation using a hash table */ /* The distribution includes a separate file, Objects/dictnotes.txt, describing explorations into dictionary design and optimization. It covers typical dictionary use patterns, the parameters for tuning dictionaries, and several ideas for possible optimizations. */ /* PyDictKeysObject This implements the dictionary's hashtable. As of Python 3.6, this is compact and ordered. Basic idea is described here: * https://mail.python.org/pipermail/python-dev/2012-December/123028.html * https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html layout: +---------------+ | dk_refcnt | | dk_size | | dk_lookup | | dk_usable | | dk_nentries | +---------------+ | dk_indices | | | +---------------+ | dk_entries | | | +---------------+ dk_indices is actual hashtable. It holds index in entries, or DKIX_EMPTY(-1) or DKIX_DUMMY(-2). Size of indices is dk_size. Type of each index in indices is vary on dk_size: * int8 for dk_size <= 128 * int16 for 256 <= dk_size <= 2**15 * int32 for 2**16 <= dk_size <= 2**31 * int64 for 2**32 <= dk_size dk_entries is array of PyDictKeyEntry. Its size is USABLE_FRACTION(dk_size). DK_ENTRIES(dk) can be used to get pointer to entries. NOTE: Since negative value is used for DKIX_EMPTY and DKIX_DUMMY, type of dk_indices entry is signed integer and int16 is used for table which dk_size == 256. */ /* The DictObject can be in one of two forms. Either: A combined table: ma_values == NULL, dk_refcnt == 1. Values are stored in the me_value field of the PyDictKeysObject. Or: A split table: ma_values != NULL, dk_refcnt >= 1 Values are stored in the ma_values array. Only string (unicode) keys are allowed. All dicts sharing same key must have same insertion order. There are four kinds of slots in the table (slot is index, and DK_ENTRIES(keys)[index] if index >= 0): 1. Unused. index == DKIX_EMPTY Does not hold an active (key, value) pair now and never did. Unused can transition to Active upon key insertion. This is each slot's initial state. 2. Active. index >= 0, me_key != NULL and me_value != NULL Holds an active (key, value) pair. Active can transition to Dummy or Pending upon key deletion (for combined and split tables respectively). This is the only case in which me_value != NULL. 3. Dummy. index == DKIX_DUMMY (combined only) Previously held an active (key, value) pair, but that was deleted and an active pair has not yet overwritten the slot. Dummy can transition to Active upon key insertion. Dummy slots cannot be made Unused again else the probe sequence in case of collision would have no way to know they were once active. 4. Pending. index >= 0, key != NULL, and value == NULL (split only) Not yet inserted in split-table. */ /* Preserving insertion order It's simple for combined table. Since dk_entries is mostly append only, we can get insertion order by just iterating dk_entries. One exception is .popitem(). It removes last item in dk_entries and decrement dk_nentries to achieve amortized O(1). Since there are DKIX_DUMMY remains in dk_indices, we can't increment dk_usable even though dk_nentries is decremented. In split table, inserting into pending entry is allowed only for dk_entries[ix] where ix == mp->ma_used. Inserting into other index and deleting item cause converting the dict to the combined table. */ /* PyDict_MINSIZE is the starting size for any new dict. * 8 allows dicts with no more than 5 active entries; experiments suggested * this suffices for the majority of dicts (consisting mostly of usually-small * dicts created to pass keyword arguments). * Making this 8, rather than 4 reduces the number of resizes for most * dictionaries, without any significant extra memory use. */ #define PyDict_MINSIZE 8 #include "Python.h" #include "dict-common.h" #include "stringlib/eq.h" // unicode_eq() /*[clinic input] class dict "PyDictObject *" "&PyDict_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=f157a5a0ce9589d6]*/ /* To ensure the lookup algorithm terminates, there must be at least one Unused slot (NULL key) in the table. To avoid slowing down lookups on a near-full table, we resize the table when it's USABLE_FRACTION (currently two-thirds) full. */ #define PERTURB_SHIFT 5 /* Major subtleties ahead: Most hash schemes depend on having a "good" hash function, in the sense of simulating randomness. Python doesn't: its most important hash functions (for ints) are very regular in common cases: >>>[hash(i) for i in range(4)] [0, 1, 2, 3] This isn't necessarily bad! To the contrary, in a table of size 2**i, taking the low-order i bits as the initial table index is extremely fast, and there are no collisions at all for dicts indexed by a contiguous range of ints. So this gives better-than-random behavior in common cases, and that's very desirable. OTOH, when collisions occur, the tendency to fill contiguous slices of the hash table makes a good collision resolution strategy crucial. Taking only the last i bits of the hash code is also vulnerable: for example, consider the list [i << 16 for i in range(20000)] as a set of keys. Since ints are their own hash codes, and this fits in a dict of size 2**15, the last 15 bits of every hash code are all 0: they *all* map to the same table index. But catering to unusual cases should not slow the usual ones, so we just take the last i bits anyway. It's up to collision resolution to do the rest. If we *usually* find the key we're looking for on the first try (and, it turns out, we usually do -- the table load factor is kept under 2/3, so the odds are solidly in our favor), then it makes best sense to keep the initial index computation dirt cheap. The first half of collision resolution is to visit table indices via this recurrence: j = ((5*j) + 1) mod 2**i For any initial j in range(2**i), repeating that 2**i times generates each int in range(2**i) exactly once (see any text on random-number generation for proof). By itself, this doesn't help much: like linear probing (setting j += 1, or j -= 1, on each loop trip), it scans the table entries in a fixed order. This would be bad, except that's not the only thing we do, and it's actually *good* in the common cases where hash keys are consecutive. In an example that's really too small to make this entirely clear, for a table of size 2**3 the order of indices is: 0 -> 1 -> 6 -> 7 -> 4 -> 5 -> 2 -> 3 -> 0 [and here it's repeating] If two things come in at index 5, the first place we look after is index 2, not 6, so if another comes in at index 6 the collision at 5 didn't hurt it. Linear probing is deadly in this case because there the fixed probe order is the *same* as the order consecutive keys are likely to arrive. But it's extremely unlikely hash codes will follow a 5*j+1 recurrence by accident, and certain that consecutive hash codes do not. The other half of the strategy is to get the other bits of the hash code into play. This is done by initializing a (unsigned) vrbl "perturb" to the full hash code, and changing the recurrence to: perturb >>= PERTURB_SHIFT; j = (5*j) + 1 + perturb; use j % 2**i as the next table index; Now the probe sequence depends (eventually) on every bit in the hash code, and the pseudo-scrambling property of recurring on 5*j+1 is more valuable, because it quickly magnifies small differences in the bits that didn't affect the initial index. Note that because perturb is unsigned, if the recurrence is executed often enough perturb eventually becomes and remains 0. At that point (very rarely reached) the recurrence is on (just) 5*j+1 again, and that's certain to find an empty slot eventually (since it generates every int in range(2**i), and we make sure there's always at least one empty slot). Selecting a good value for PERTURB_SHIFT is a balancing act. You want it small so that the high bits of the hash code continue to affect the probe sequence across iterations; but you want it large so that in really bad cases the high-order hash bits have an effect on early iterations. 5 was "the best" in minimizing total collisions across experiments Tim Peters ran (on both normal and pathological cases), but 4 and 6 weren't significantly worse. Historical: Reimer Behrends contributed the idea of using a polynomial-based approach, using repeated multiplication by x in GF(2**n) where an irreducible polynomial for each table size was chosen such that x was a primitive root. Christian Tismer later extended that to use division by x instead, as an efficient way to get the high bits of the hash code into play. This scheme also gave excellent collision statistics, but was more expensive: two if-tests were required inside the loop; computing "the next" index took about the same number of operations but without as much potential parallelism (e.g., computing 5*j can go on at the same time as computing 1+perturb in the above, and then shifting perturb can be done while the table index is being masked); and the PyDictObject struct required a member to hold the table's polynomial. In Tim's experiments the current scheme ran faster, produced equally good collision statistics, needed less code & used less memory. */ /* forward declarations */ static Py_ssize_t lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static Py_ssize_t lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); /*Global counter used to set ma_version_tag field of dictionary. * It is incremented each time that a dictionary is created and each * time that a dictionary is modified. */ static uint64_t pydict_global_version = 0; #define DICT_NEXT_VERSION() (++pydict_global_version) #include "clinic/dictobject.c.h" #define DK_SIZE(dk) ((dk)->dk_size) #if SIZEOF_VOID_P > 4 #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : DK_SIZE(dk) <= 0xffffffff ? \ 4 : sizeof(int64_t)) #else #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : sizeof(int32_t)) #endif #define DK_ENTRIES(dk) \ ((PyDictKeyEntry*)(&((int8_t*)((dk)->dk_indices))[DK_SIZE(dk) * DK_IXSIZE(dk)])) #define DK_MASK(dk) (((dk)->dk_size)-1) #define IS_POWER_OF_2(x) (((x) & (x-1)) == 0) static void free_keys_object(PyDictKeysObject *keys); static inline void dictkeys_incref(PyDictKeysObject *dk) { #ifdef Py_REF_DEBUG _Py_RefTotal++; #endif dk->dk_refcnt++; } static inline void dictkeys_decref(PyDictKeysObject *dk) { assert(dk->dk_refcnt > 0); #ifdef Py_REF_DEBUG _Py_RefTotal--; #endif if (--dk->dk_refcnt == 0) { free_keys_object(dk); } } /* lookup indices. returns DKIX_EMPTY, DKIX_DUMMY, or ix >=0 */ static inline Py_ssize_t dictkeys_get_index(const PyDictKeysObject *keys, Py_ssize_t i) { Py_ssize_t s = DK_SIZE(keys); Py_ssize_t ix; if (s <= 0xff) { const int8_t *indices = (const int8_t*)(keys->dk_indices); ix = indices[i]; } else if (s <= 0xffff) { const int16_t *indices = (const int16_t*)(keys->dk_indices); ix = indices[i]; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { const int64_t *indices = (const int64_t*)(keys->dk_indices); ix = indices[i]; } #endif else { const int32_t *indices = (const int32_t*)(keys->dk_indices); ix = indices[i]; } assert(ix >= DKIX_DUMMY); return ix; } /* write to indices. */ static inline void dictkeys_set_index(PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix) { Py_ssize_t s = DK_SIZE(keys); assert(ix >= DKIX_DUMMY); if (s <= 0xff) { int8_t *indices = (int8_t*)(keys->dk_indices); assert(ix <= 0x7f); indices[i] = (char)ix; } else if (s <= 0xffff) { int16_t *indices = (int16_t*)(keys->dk_indices); assert(ix <= 0x7fff); indices[i] = (int16_t)ix; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { int64_t *indices = (int64_t*)(keys->dk_indices); indices[i] = ix; } #endif else { int32_t *indices = (int32_t*)(keys->dk_indices); assert(ix <= 0x7fffffff); indices[i] = (int32_t)ix; } } /* USABLE_FRACTION is the maximum dictionary load. * Increasing this ratio makes dictionaries more dense resulting in more * collisions. Decreasing it improves sparseness at the expense of spreading * indices over more cache lines and at the cost of total memory consumed. * * USABLE_FRACTION must obey the following: * (0 < USABLE_FRACTION(n) < n) for all n >= 2 * * USABLE_FRACTION should be quick to calculate. * Fractions around 1/2 to 2/3 seem to work well in practice. */ #define USABLE_FRACTION(n) (((n) << 1)/3) /* Find the smallest dk_size >= minsize. */ static inline Py_ssize_t calculate_keysize(Py_ssize_t minsize) { #if SIZEOF_LONG == SIZEOF_SIZE_T minsize = (minsize | PyDict_MINSIZE) - 1; return 1LL << _Py_bit_length(minsize | (PyDict_MINSIZE-1)); #elif defined(_MSC_VER) // On 64bit Windows, sizeof(long) == 4. minsize = (minsize | PyDict_MINSIZE) - 1; unsigned long msb; _BitScanReverse64(&msb, (uint64_t)minsize); return 1LL << (msb + 1); #else Py_ssize_t size; for (size = PyDict_MINSIZE; size < minsize && size > 0; size <<= 1) ; return size; #endif } /* estimate_keysize is reverse function of USABLE_FRACTION. * * This can be used to reserve enough size to insert n entries without * resizing. */ static inline Py_ssize_t estimate_keysize(Py_ssize_t n) { return calculate_keysize((n*3 + 1) / 2); } /* GROWTH_RATE. Growth rate upon hitting maximum load. * Currently set to used*3. * This means that dicts double in size when growing without deletions, * but have more head room when the number of deletions is on a par with the * number of insertions. See also bpo-17563 and bpo-33205. * * GROWTH_RATE was set to used*4 up to version 3.2. * GROWTH_RATE was set to used*2 in version 3.3.0 * GROWTH_RATE was set to used*2 + capacity/2 in 3.4.0-3.6.0. */ #define GROWTH_RATE(d) ((d)->ma_used*3) /* This immutable, empty PyDictKeysObject is used for PyDict_Clear() * (which cannot fail and thus can do no allocation). */ static PyDictKeysObject empty_keys_struct = { 1, /* dk_refcnt */ 1, /* dk_size */ lookdict_unicode_nodummy, /* dk_lookup */ 0, /* dk_usable (immutable) */ 0, /* dk_nentries */ {DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY}, /* dk_indices */ }; static PyObject *empty_values[1] = { NULL }; #define Py_EMPTY_KEYS &empty_keys_struct /* Uncomment to check the dict content in _PyDict_CheckConsistency() */ /* #define DEBUG_PYDICT */ #ifdef DEBUG_PYDICT # define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 1)) #else # define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 0)) #endif static int _PyDict_CheckConsistency(PyObject *op, int check_content) { #define CHECK(expr) \ do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0) assert(op != NULL); CHECK(PyAnyDict_Check(op)); PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; int splitted = _PyDict_HasSplitTable(mp); Py_ssize_t usable = USABLE_FRACTION(keys->dk_size); CHECK(0 <= mp->ma_used && mp->ma_used <= usable); CHECK(IS_POWER_OF_2(keys->dk_size)); CHECK(0 <= keys->dk_usable && keys->dk_usable <= usable); CHECK(0 <= keys->dk_nentries && keys->dk_nentries <= usable); CHECK(keys->dk_usable + keys->dk_nentries <= usable); if (!splitted) { /* combined table */ CHECK(keys->dk_refcnt == 1); } if (check_content) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i; for (i=0; i < keys->dk_size; i++) { Py_ssize_t ix = dictkeys_get_index(keys, i); CHECK(DKIX_DUMMY <= ix && ix <= usable); } for (i=0; i < usable; i++) { PyDictKeyEntry *entry = &entries[i]; PyObject *key = entry->me_key; if (key != NULL) { if (PyUnicode_CheckExact(key)) { Py_hash_t hash = ((PyASCIIObject *)key)->hash; CHECK(hash != -1); CHECK(entry->me_hash == hash); } else { /* test_dict fails if PyObject_Hash() is called again */ CHECK(entry->me_hash != -1); } if (!splitted) { CHECK(entry->me_value != NULL); } } if (splitted) { CHECK(entry->me_value == NULL); } } if (splitted) { /* splitted table */ for (i=0; i < mp->ma_used; i++) { CHECK(mp->ma_values[i] != NULL); } } } return 1; #undef CHECK } static PyDictKeysObject* new_keys_object(Py_ssize_t size) { PyDictKeysObject *dk; Py_ssize_t es, usable; assert(size >= PyDict_MINSIZE); assert(IS_POWER_OF_2(size)); usable = USABLE_FRACTION(size); if (size <= 0xff) { es = 1; } else if (size <= 0xffff) { es = 2; } #if SIZEOF_VOID_P > 4 else if (size <= 0xffffffff) { es = 4; } #endif else { es = sizeof(Py_ssize_t); } dk = PyObject_Malloc(sizeof(PyDictKeysObject) + es * size + sizeof(PyDictKeyEntry) * usable); if (dk == NULL) { PyErr_NoMemory(); return NULL; } #ifdef Py_REF_DEBUG _Py_RefTotal++; #endif dk->dk_refcnt = 1; dk->dk_size = size; dk->dk_usable = usable; dk->dk_lookup = lookdict_unicode_nodummy; dk->dk_nentries = 0; memset(&dk->dk_indices[0], 0xff, es * size); memset(DK_ENTRIES(dk), 0, sizeof(PyDictKeyEntry) * usable); return dk; } static void free_keys_object(PyDictKeysObject *keys) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n; for (i = 0, n = keys->dk_nentries; i < n; i++) { Py_XDECREF(entries[i].me_key); Py_XDECREF(entries[i].me_value); } PyObject_Free(keys); } #define new_values(size) PyMem_NEW(PyObject *, size) #define free_values(values) PyMem_Free(values) static Py_ssize_t _d_PyDict_KeysSize(PyDictKeysObject *keys) { return (sizeof(PyDictKeysObject) + DK_IXSIZE(keys) * DK_SIZE(keys) + USABLE_FRACTION(DK_SIZE(keys)) * sizeof(PyDictKeyEntry)); } static PyDictKeysObject * clone_combined_dict_keys(PyDictObject *orig) { assert(PyAnyDict_Check(orig)); assert( Py_TYPE(orig)->tp_iter == PyDict_Type.tp_iter || Py_TYPE(orig)->tp_iter == (getiterfunc)frozendict_iter ); assert(orig->ma_values == NULL); assert(orig->ma_keys->dk_refcnt == 1); Py_ssize_t keys_size = _d_PyDict_KeysSize(orig->ma_keys); PyDictKeysObject *keys = PyObject_Malloc(keys_size); if (keys == NULL) { PyErr_NoMemory(); return NULL; } memcpy(keys, orig->ma_keys, keys_size); /* After copying key/value pairs, we need to incref all keys and values and they are about to be co-owned by a new dict object. */ PyDictKeyEntry *ep0 = DK_ENTRIES(keys); Py_ssize_t n = keys->dk_nentries; for (Py_ssize_t i = 0; i < n; i++) { PyDictKeyEntry *entry = &ep0[i]; PyObject *value = entry->me_value; Py_INCREF(value); Py_INCREF(entry->me_key); } /* Since we copied the keys table we now have an extra reference in the system. Manually call increment _Py_RefTotal to signal that we have it now; calling dictkeys_incref would be an error as keys->dk_refcnt is already set to 1 (after memcpy). */ #ifdef Py_REF_DEBUG _Py_RefTotal++; #endif return keys; } /* The basic lookup function used by all operations. This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4. Open addressing is preferred over chaining since the link overhead for chaining would be substantial (100% with typical malloc overhead). The initial probe index is computed as hash mod the table size. Subsequent probe indices are computed as explained earlier. All arithmetic on hash should ignore overflow. The details in this version are due to Tim Peters, building on many past contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and Christian Tismer. lookdict() is general-purpose, and may return DKIX_ERROR if (and only if) a comparison raises an exception. lookdict_unicode() below is specialized to string keys, comparison of which can never raise an exception; that function can never return DKIX_ERROR when key is string. Otherwise, it falls back to lookdict(). lookdict_unicode_nodummy is further specialized for string keys that cannot be the value. For both, when the key isn't found a DKIX_EMPTY is returned. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { size_t i, mask, perturb; PyDictKeysObject *dk; PyDictKeyEntry *ep0; top: dk = mp->ma_keys; ep0 = DK_ENTRIES(dk); mask = DK_MASK(dk); perturb = hash; i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(dk, i); if (ix == DKIX_EMPTY) { *value_addr = NULL; return ix; } if (ix >= 0) { PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); if (ep->me_key == key) { *value_addr = ep->me_value; return ix; } if (ep->me_hash == hash) { PyObject *startkey = ep->me_key; Py_INCREF(startkey); int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); Py_DECREF(startkey); if (cmp < 0) { *value_addr = NULL; return DKIX_ERROR; } if (dk == mp->ma_keys && ep->me_key == startkey) { if (cmp > 0) { *value_addr = ep->me_value; return ix; } } else { /* The dict was mutated, restart */ goto top; } } } perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; } Py_UNREACHABLE(); } /* Faster version of lookdict_unicode when it is known that no keys * will be present. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { assert(mp->ma_values == NULL); /* Make sure this function doesn't have to handle non-unicode keys, including subclasses of str; e.g., one reason to subclass unicodes is to override __eq__, and for speed we don't cater to that here. */ if (!PyUnicode_CheckExact(key)) { return lookdict(mp, key, hash, value_addr); } PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); size_t mask = DK_MASK(mp->ma_keys); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i); assert (ix != DKIX_DUMMY); if (ix == DKIX_EMPTY) { *value_addr = NULL; return DKIX_EMPTY; } PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { *value_addr = ep->me_value; return ix; } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } #define MAINTAIN_TRACKING(mp, key, value) \ do { \ if (!_PyObject_GC_IS_TRACKED(mp)) { \ if (_PyObject_GC_MAY_BE_TRACKED(key) || \ _PyObject_GC_MAY_BE_TRACKED(value)) { \ PyObject_GC_Track(mp); \ } \ } \ } while(0) /* Internal function to find slot for an item from its hash when it is known that the key is not present in the dict. The dict must be combined. */ static Py_ssize_t find_empty_slot(PyDictKeysObject *keys, Py_hash_t hash) { assert(keys != NULL); const size_t mask = DK_MASK(keys); size_t i = hash & mask; Py_ssize_t ix = dictkeys_get_index(keys, i); for (size_t perturb = hash; ix >= 0;) { perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; ix = dictkeys_get_index(keys, i); } return i; } /* Internal routine used by dictresize() to build a hashtable of entries. */ static void build_indices(PyDictKeysObject *keys, PyDictKeyEntry *ep, Py_ssize_t n) { size_t mask = (size_t)DK_SIZE(keys) - 1; for (Py_ssize_t ix = 0; ix != n; ix++, ep++) { Py_hash_t hash = ep->me_hash; size_t i = hash & mask; for (size_t perturb = hash; dictkeys_get_index(keys, i) != DKIX_EMPTY;) { perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } dictkeys_set_index(keys, i, ix); } } static int _d_PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue, Py_hash_t *phash) { Py_ssize_t i; PyDictObject *mp; PyDictKeyEntry *entry_ptr; PyObject *value; if (!PyAnyDict_Check(op)) return 0; mp = (PyDictObject *)op; i = *ppos; if (mp->ma_values) { if (i < 0 || i >= mp->ma_used) return 0; /* values of split table is always dense */ entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; value = mp->ma_values[i]; assert(value != NULL); } else { Py_ssize_t n = mp->ma_keys->dk_nentries; if (i < 0 || i >= n) return 0; entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) return 0; value = entry_ptr->me_value; } *ppos = i+1; if (pkey) *pkey = entry_ptr->me_key; if (phash) *phash = entry_ptr->me_hash; if (pvalue) *pvalue = value; return 1; } /* Methods */ static void dict_dealloc(PyDictObject *mp) { PyObject **values = mp->ma_values; PyDictKeysObject *keys = mp->ma_keys; Py_ssize_t i, n; /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(mp); Py_TRASHCAN_BEGIN(mp, dict_dealloc) if (values != NULL) { if (values != empty_values) { for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) { Py_XDECREF(values[i]); } free_values(values); } dictkeys_decref(keys); } else if (keys != NULL) { assert(keys->dk_refcnt == 1 || keys == Py_EMPTY_KEYS); dictkeys_decref(keys); } Py_TYPE(mp)->tp_free((PyObject *)mp); Py_TRASHCAN_END } static PyObject * dict_repr(PyDictObject *mp) { Py_ssize_t i; PyObject *key = NULL, *value = NULL; _PyUnicodeWriter writer; int first; i = Py_ReprEnter((PyObject *)mp); if (i != 0) { return i > 0 ? PyUnicode_FromString("{...}") : NULL; } if (mp->ma_used == 0) { Py_ReprLeave((PyObject *)mp); return PyUnicode_FromString("{}"); } _PyUnicodeWriter_Init(&writer); writer.overallocate = 1; /* "{" + "1: 2" + ", 3: 4" * (len - 1) + "}" */ writer.min_length = 1 + 4 + (2 + 4) * (mp->ma_used - 1) + 1; if (_PyUnicodeWriter_WriteChar(&writer, '{') < 0) goto error; /* Do repr() on each key+value pair, and insert ": " between them. Note that repr may mutate the dict. */ i = 0; first = 1; while (_d_PyDict_Next((PyObject *)mp, &i, &key, &value, NULL)) { PyObject *s; int res; /* Prevent repr from deleting key or value during key format. */ Py_INCREF(key); Py_INCREF(value); if (!first) { if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0) goto error; } first = 0; s = PyObject_Repr(key); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; if (_PyUnicodeWriter_WriteASCIIString(&writer, ": ", 2) < 0) goto error; s = PyObject_Repr(value); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; Py_CLEAR(key); Py_CLEAR(value); } writer.overallocate = 0; if (_PyUnicodeWriter_WriteChar(&writer, '}') < 0) goto error; Py_ReprLeave((PyObject *)mp); return _PyUnicodeWriter_Finish(&writer); error: Py_ReprLeave((PyObject *)mp); _PyUnicodeWriter_Dealloc(&writer); Py_XDECREF(key); Py_XDECREF(value); return NULL; } static Py_ssize_t dict_length(PyDictObject *mp) { return mp->ma_used; } static PyObject * dict_subscript(PyDictObject *mp, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || value == NULL) { if (!PyAnyDict_CheckExact(mp)) { /* Look up __missing__ method if we're a subclass. */ PyObject *missing, *res; _Py_IDENTIFIER(__missing__); missing = _PyObject_LookupSpecial((PyObject *)mp, &PyId___missing__); if (missing != NULL) { res = PyObject_CallOneArg(missing, key); Py_DECREF(missing); return res; } else if (PyErr_Occurred()) return NULL; } _PyErr_SetKeyError(key); return NULL; } Py_INCREF(value); return value; } static PyObject * dict_richcompare(PyObject *v, PyObject *w, int op) { int cmp; PyObject *res; if (!PyAnyDict_Check(v) || !PyAnyDict_Check(w)) { res = Py_NotImplemented; } else if (op == Py_EQ || op == Py_NE) { cmp = frozendict_equal((PyDictObject *)v, (PyDictObject *)w); if (cmp < 0) return NULL; res = (cmp == (op == Py_EQ)) ? Py_True : Py_False; } else res = Py_NotImplemented; Py_INCREF(res); return res; } /*[clinic input] @coexist dict.__contains__ key: object / True if the dictionary has the specified key, else False. [clinic start generated code]*/ static PyObject * dict___contains__(PyDictObject *self, PyObject *key) /*[clinic end generated code: output=a3d03db709ed6e6b input=fe1cb42ad831e820]*/ { register PyDictObject *mp = self; Py_hash_t hash; Py_ssize_t ix; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || value == NULL) Py_RETURN_FALSE; Py_RETURN_TRUE; } /*[clinic input] dict.get key: object default: object = None / Return the value for key if key is in the dictionary, else default. [clinic start generated code]*/ static PyObject * dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value) /*[clinic end generated code: output=bba707729dee05bf input=279ddb5790b6b107]*/ { PyObject *val = NULL; Py_hash_t hash; Py_ssize_t ix; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (self->ma_keys->dk_lookup) (self, key, hash, &val); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || val == NULL) { val = default_value; } Py_INCREF(val); return val; } static int dict_traverse(PyObject *op, visitproc visit, void *arg) { PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n = keys->dk_nentries; if (keys->dk_lookup == lookdict) { for (i = 0; i < n; i++) { if (entries[i].me_value != NULL) { Py_VISIT(entries[i].me_value); Py_VISIT(entries[i].me_key); } } } else { if (mp->ma_values != NULL) { for (i = 0; i < n; i++) { Py_VISIT(mp->ma_values[i]); } } else { for (i = 0; i < n; i++) { Py_VISIT(entries[i].me_value); } } } return 0; } static PyObject *dictiter_new(PyDictObject *, PyTypeObject *); static PyObject * dict_sizeof(PyDictObject *mp, PyObject *Py_UNUSED(ignored)) { return PyLong_FromSsize_t(_PyDict_SizeOf(mp)); } PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]"); PyDoc_STRVAR(sizeof__doc__, "D.__sizeof__() -> size of D in memory, in bytes"); PyDoc_STRVAR(copy__doc__, "D.copy() -> a shallow copy of D"); PyDoc_STRVAR(keys__doc__, "D.keys() -> a set-like object providing a view on D's keys"); PyDoc_STRVAR(items__doc__, "D.items() -> a set-like object providing a view on D's items"); PyDoc_STRVAR(values__doc__, "D.values() -> an object providing a view on D's values"); /* Hack to implement "key in dict" */ static PySequenceMethods dict_as_sequence = { 0, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ PyDict_Contains, /* sq_contains */ 0, /* sq_inplace_concat */ 0, /* sq_inplace_repeat */ }; /* Dictionary iterator types */ typedef struct { PyObject_HEAD PyDictObject *di_dict; /* Set to NULL when iterator is exhausted */ Py_ssize_t di_used; Py_ssize_t di_pos; PyObject* di_result; /* reusable result tuple for iteritems */ Py_ssize_t len; } dictiterobject; static PyObject * dictiter_new(PyDictObject *dict, PyTypeObject *itertype) { dictiterobject *di; di = PyObject_GC_New(dictiterobject, itertype); if (di == NULL) { return NULL; } Py_INCREF(dict); di->di_dict = dict; di->di_used = dict->ma_used; di->len = dict->ma_used; if (itertype == &PyDictRevIterKey_Type || itertype == &PyDictRevIterItem_Type || itertype == &PyDictRevIterValue_Type) { if (dict->ma_values) { di->di_pos = dict->ma_used - 1; } else { di->di_pos = dict->ma_keys->dk_nentries - 1; } } else { di->di_pos = 0; } if ( itertype == &PyFrozenDictIterItem_Type || itertype == &PyDictRevIterItem_Type ) { di->di_result = PyTuple_Pack(2, Py_None, Py_None); if (di->di_result == NULL) { Py_DECREF(di); return NULL; } } else { di->di_result = NULL; } PyObject_GC_Track(di); return (PyObject *)di; } static void dictiter_dealloc(dictiterobject *di) { /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(di); Py_XDECREF(di->di_dict); Py_XDECREF(di->di_result); PyObject_GC_Del(di); } static int dictiter_traverse(dictiterobject *di, visitproc visit, void *arg) { Py_VISIT(di->di_dict); Py_VISIT(di->di_result); return 0; } static PyObject * dictiter_len(dictiterobject *di, PyObject *Py_UNUSED(ignored)) { Py_ssize_t len = 0; if (di->di_dict != NULL && di->di_used == di->di_dict->ma_used) len = di->len; return PyLong_FromSize_t(len); } PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); static PyObject * dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyMethodDef dictiter_methods[] = { {"__length_hint__", (PyCFunction)(void(*)(void))dictiter_len, METH_NOARGS, length_hint_doc}, {"__reduce__", (PyCFunction)(void(*)(void))dictiter_reduce, METH_NOARGS, reduce_doc}, {NULL, NULL} /* sentinel */ }; /* dictreviter */ /*[clinic input] dict.__reversed__ Return a reverse iterator over the dict keys. [clinic start generated code]*/ static PyObject * dict___reversed___impl(PyDictObject *self) /*[clinic end generated code: output=e674483336d1ed51 input=23210ef3477d8c4d]*/ { assert (PyAnyDict_Check(self)); return dictiter_new(self, &PyDictRevIterKey_Type); } static PyObject * dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)) { _Py_IDENTIFIER(iter); /* copy the iterator state */ dictiterobject tmp = *di; Py_XINCREF(tmp.di_dict); PyObject *list = PySequence_List((PyObject*)&tmp); Py_XDECREF(tmp.di_dict); if (list == NULL) { return NULL; } return Py_BuildValue("N(N)", _PyEval_GetBuiltinId(&PyId_iter), list); } /***********************************************/ /* View objects for keys(), items(), values(). */ /***********************************************/ /* The instance lay-out is the same for all three; but the type differs. */ static void dictview_dealloc(_PyDictViewObject *dv) { /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(dv); Py_XDECREF(dv->dv_dict); PyObject_GC_Del(dv); } static int dictview_traverse(_PyDictViewObject *dv, visitproc visit, void *arg) { Py_VISIT(dv->dv_dict); return 0; } static Py_ssize_t dictview_len(_PyDictViewObject *dv) { Py_ssize_t len = 0; if (dv->dv_dict != NULL) len = dv->dv_dict->ma_used; return len; } static PyObject * _d_PyDictView_New(PyObject *dict, PyTypeObject *type) { _PyDictViewObject *dv; if (dict == NULL) { PyErr_BadInternalCall(); return NULL; } if (!PyAnyDict_Check(dict)) { /* XXX Get rid of this restriction later */ PyErr_Format(PyExc_TypeError, "%s() requires a dict argument, not '%s'", type->tp_name, Py_TYPE(dict)->tp_name); return NULL; } dv = PyObject_GC_New(_PyDictViewObject, type); if (dv == NULL) return NULL; Py_INCREF(dict); dv->dv_dict = (PyDictObject *)dict; PyObject_GC_Track(dv); return (PyObject *)dv; } static PyObject * dictview_mapping(PyObject *view, void *Py_UNUSED(ignored)) { assert(view != NULL); assert(PyAnyDictKeys_Check(view) || PyAnyDictValues_Check(view) || PyAnyDictItems_Check(view)); PyObject *mapping = (PyObject *)((_PyDictViewObject *)view)->dv_dict; return PyDictProxy_New(mapping); } static PyGetSetDef dictview_getset[] = { {"mapping", dictview_mapping, (setter)NULL, "dictionary that this view refers to", NULL}, {0} }; /* TODO(guido): The views objects are not complete: * support more set operations * support arbitrary mappings? - either these should be static or exported in dictobject.h - if public then they should probably be in builtins */ /* Return 1 if self is a subset of other, iterating over self; 0 if not; -1 if an error occurred. */ static int all_contained_in(PyObject *self, PyObject *other) { PyObject *iter = PyObject_GetIter(self); int ok = 1; if (iter == NULL) return -1; for (;;) { PyObject *next = PyIter_Next(iter); if (next == NULL) { if (PyErr_Occurred()) ok = -1; break; } ok = PySequence_Contains(other, next); Py_DECREF(next); if (ok <= 0) break; } Py_DECREF(iter); return ok; } static PyObject * dictview_richcompare(PyObject *self, PyObject *other, int op) { Py_ssize_t len_self, len_other; int ok; PyObject *result; assert(self != NULL); assert(PyAnyDictViewSet_Check(self)); assert(other != NULL); if (!PyAnySet_Check(other) && !PyAnyDictViewSet_Check(other)) Py_RETURN_NOTIMPLEMENTED; len_self = PyObject_Size(self); if (len_self < 0) return NULL; len_other = PyObject_Size(other); if (len_other < 0) return NULL; ok = 0; switch(op) { case Py_NE: case Py_EQ: if (len_self == len_other) ok = all_contained_in(self, other); if (op == Py_NE && ok >= 0) ok = !ok; break; case Py_LT: if (len_self < len_other) ok = all_contained_in(self, other); break; case Py_LE: if (len_self <= len_other) ok = all_contained_in(self, other); break; case Py_GT: if (len_self > len_other) ok = all_contained_in(other, self); break; case Py_GE: if (len_self >= len_other) ok = all_contained_in(other, self); break; } if (ok < 0) return NULL; result = ok ? Py_True : Py_False; Py_INCREF(result); return result; } static PyObject * dictview_repr(_PyDictViewObject *dv) { PyObject *seq; PyObject *result = NULL; Py_ssize_t rc; rc = Py_ReprEnter((PyObject *)dv); if (rc != 0) { return rc > 0 ? PyUnicode_FromString("...") : NULL; } seq = PySequence_List((PyObject *)dv); if (seq == NULL) { goto Done; } result = PyUnicode_FromFormat("%s(%R)", Py_TYPE(dv)->tp_name, seq); Py_DECREF(seq); Done: Py_ReprLeave((PyObject *)dv); return result; } /*** dict_keys ***/ static int dictkeys_contains(_PyDictViewObject *dv, PyObject *obj) { if (dv->dv_dict == NULL) return 0; return PyDict_Contains((PyObject *)dv->dv_dict, obj); } static PySequenceMethods dictkeys_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictkeys_contains, /* sq_contains */ }; // Create an set object from dictviews object. // Returns a new reference. // This utility function is used by set operations. static PyObject* dictviews_to_set(PyObject *self) { PyObject *left = self; if (PyAnyDictKeys_Check(self)) { // PySet_New() has fast path for the dict object. PyObject *dict = (PyObject *)((_PyDictViewObject *)self)->dv_dict; if (PyAnyDict_CheckExact(dict)) { left = dict; } } return PySet_New(left); } static PyObject* dictviews_sub(PyObject *self, PyObject *other) { PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } _Py_IDENTIFIER(difference_update); PyObject *tmp = _PyObject_CallMethodIdOneArg( result, &PyId_difference_update, other); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static int dictitems_contains(_PyDictViewObject *dv, PyObject *obj); static PyObject * _d_PyDictView_Intersect(PyObject* self, PyObject *other) { PyObject *result; PyObject *it; PyObject *key; Py_ssize_t len_self; int rv; int (*dict_contains)(_PyDictViewObject *, PyObject *); /* Python interpreter swaps parameters when dict view is on right side of & */ if (!PyAnyDictViewSet_Check(self)) { PyObject *tmp = other; other = self; self = tmp; } len_self = dictview_len((_PyDictViewObject *)self); /* if other is a set and self is smaller than other, reuse set intersection logic */ if (PySet_CheckExact(other) && len_self <= PyObject_Size(other)) { _Py_IDENTIFIER(intersection); return _PyObject_CallMethodIdObjArgs(other, &PyId_intersection, self, NULL); } /* if other is another dict view, and it is bigger than self, swap them */ if (PyAnyDictViewSet_Check(other)) { Py_ssize_t len_other = dictview_len((_PyDictViewObject *)other); if (len_other > len_self) { PyObject *tmp = other; other = self; self = tmp; } } /* at this point, two things should be true 1. self is a dictview 2. if other is a dictview then it is smaller than self */ result = PySet_New(NULL); if (result == NULL) return NULL; it = PyObject_GetIter(other); if (it == NULL) { Py_DECREF(result); return NULL; } if (PyAnyDictKeys_Check(self)) { dict_contains = dictkeys_contains; } /* else PyDictItems_Check(self) */ else { dict_contains = dictitems_contains; } while ((key = PyIter_Next(it)) != NULL) { rv = dict_contains((_PyDictViewObject *)self, key); if (rv < 0) { goto error; } if (rv) { if (PySet_Add(result, key)) { goto error; } } Py_DECREF(key); } Py_DECREF(it); if (PyErr_Occurred()) { Py_DECREF(result); return NULL; } return result; error: Py_DECREF(it); Py_DECREF(result); Py_DECREF(key); return NULL; } static PyObject* dictviews_or(PyObject* self, PyObject *other) { PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } if (_PySet_Update(result, other) < 0) { Py_DECREF(result); return NULL; } return result; } static PyObject * dictitems_xor(PyObject *self, PyObject *other) { assert(PyAnyDictItems_Check(self)); assert(PyAnyDictItems_Check(other)); PyObject *d1 = (PyObject *)((_PyDictViewObject *)self)->dv_dict; PyObject *d2 = (PyObject *)((_PyDictViewObject *)other)->dv_dict; PyObject *temp_dict; if (PyDict_CheckExact(d1)) { temp_dict = PyDict_Copy(d1); } else { PyObject* args = PyTuple_New(1); if (args == NULL) { return NULL; } Py_INCREF(d1); PyTuple_SET_ITEM(args, 0, d1); temp_dict = PyObject_Call((PyObject *) &PyDict_Type, args, NULL); Py_DECREF(args); } if (temp_dict == NULL) { return NULL; } PyObject *result_set = PySet_New(NULL); if (result_set == NULL) { Py_CLEAR(temp_dict); return NULL; } PyObject *key = NULL, *val1 = NULL, *val2 = NULL; Py_ssize_t pos = 0; Py_hash_t hash; while (_d_PyDict_Next(d2, &pos, &key, &val2, &hash)) { Py_INCREF(key); Py_INCREF(val2); val1 = _PyDict_GetItem_KnownHash(temp_dict, key, hash); int to_delete; if (val1 == NULL) { if (PyErr_Occurred()) { goto error; } to_delete = 0; } else { Py_INCREF(val1); to_delete = PyObject_RichCompareBool(val1, val2, Py_EQ); if (to_delete < 0) { goto error; } } if (to_delete) { if (_PyDict_DelItem_KnownHash(temp_dict, key, hash) < 0) { goto error; } } else { PyObject *pair = PyTuple_Pack(2, key, val2); if (pair == NULL) { goto error; } if (PySet_Add(result_set, pair) < 0) { Py_DECREF(pair); goto error; } Py_DECREF(pair); } Py_DECREF(key); Py_XDECREF(val1); Py_DECREF(val2); } key = val1 = val2 = NULL; _Py_IDENTIFIER(items); PyObject *remaining_pairs = _PyObject_CallMethodIdNoArgs(temp_dict, &PyId_items); if (remaining_pairs == NULL) { goto error; } if (_PySet_Update(result_set, remaining_pairs) < 0) { Py_DECREF(remaining_pairs); goto error; } Py_DECREF(temp_dict); Py_DECREF(remaining_pairs); return result_set; error: Py_XDECREF(temp_dict); Py_XDECREF(result_set); Py_XDECREF(key); Py_XDECREF(val1); Py_XDECREF(val2); return NULL; } static PyObject* dictviews_xor(PyObject* self, PyObject *other) { if (PyAnyDictItems_Check(self) && PyAnyDictItems_Check(other)) { return dictitems_xor(self, other); } PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } _Py_IDENTIFIER(symmetric_difference_update); PyObject *tmp = _PyObject_CallMethodIdOneArg( result, &PyId_symmetric_difference_update, other); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static PyNumberMethods dictviews_as_number = { 0, /*nb_add*/ (binaryfunc)dictviews_sub, /*nb_subtract*/ 0, /*nb_multiply*/ 0, /*nb_remainder*/ 0, /*nb_divmod*/ 0, /*nb_power*/ 0, /*nb_negative*/ 0, /*nb_positive*/ 0, /*nb_absolute*/ 0, /*nb_bool*/ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ (binaryfunc)_d_PyDictView_Intersect,/*nb_and*/ (binaryfunc)dictviews_xor, /*nb_xor*/ (binaryfunc)dictviews_or, /*nb_or*/ }; static PyObject* dictviews_isdisjoint(PyObject *self, PyObject *other) { PyObject *it; PyObject *item = NULL; if (self == other) { if (dictview_len((_PyDictViewObject *)self) == 0) Py_RETURN_TRUE; else Py_RETURN_FALSE; } /* Iterate over the shorter object (only if other is a set, * because PySequence_Contains may be expensive otherwise): */ if (PyAnySet_Check(other) || PyAnyDictViewSet_Check(other)) { Py_ssize_t len_self = dictview_len((_PyDictViewObject *)self); Py_ssize_t len_other = PyObject_Size(other); if (len_other == -1) return NULL; if ((len_other > len_self)) { PyObject *tmp = other; other = self; self = tmp; } } it = PyObject_GetIter(other); if (it == NULL) return NULL; while ((item = PyIter_Next(it)) != NULL) { int contains = PySequence_Contains(self, item); Py_DECREF(item); if (contains == -1) { Py_DECREF(it); return NULL; } if (contains) { Py_DECREF(it); Py_RETURN_FALSE; } } Py_DECREF(it); if (PyErr_Occurred()) return NULL; /* PyIter_Next raised an exception. */ Py_RETURN_TRUE; } PyDoc_STRVAR(isdisjoint_doc, "Return True if the view and the given iterable have a null intersection."); static PyObject* dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_keys_doc, "Return a reverse iterator over the dict keys."); static PyMethodDef dictkeys_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {"__reversed__", (PyCFunction)(void(*)(void))dictkeys_reversed, METH_NOARGS, reversed_keys_doc}, {NULL, NULL} /* sentinel */ }; static PyObject * dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterKey_Type); } /*** dict_items ***/ /* Variant of PyDict_GetItem() that doesn't suppress exceptions. This returns NULL *with* an exception set if an exception occurred. It returns NULL *without* an exception set if the key wasn't present. */ static PyObject * d_PyDict_GetItemWithError(PyObject *op, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyDictObject*mp = (PyDictObject *)op; PyObject *value; if (!PyAnyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { return NULL; } } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix < 0) return NULL; return value; } static int dictitems_contains(_PyDictViewObject *dv, PyObject *obj) { int result; PyObject *key, *value, *found; if (dv->dv_dict == NULL) return 0; if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) return 0; key = PyTuple_GET_ITEM(obj, 0); value = PyTuple_GET_ITEM(obj, 1); found = d_PyDict_GetItemWithError((PyObject *)dv->dv_dict, key); if (found == NULL) { if (PyErr_Occurred()) return -1; return 0; } Py_INCREF(found); result = PyObject_RichCompareBool(found, value, Py_EQ); Py_DECREF(found); return result; } static PySequenceMethods dictitems_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictitems_contains, /* sq_contains */ }; static PyObject* dictitems_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_items_doc, "Return a reverse iterator over the dict items."); static PyMethodDef dictitems_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {"__reversed__", (PyCFunction)dictitems_reversed, METH_NOARGS, reversed_items_doc}, {NULL, NULL} /* sentinel */ }; static PyObject * dictitems_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterItem_Type); } /*** dict_values ***/ static PySequenceMethods dictvalues_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)0, /* sq_contains */ }; static PyObject* dictvalues_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_values_doc, "Return a reverse iterator over the dict values."); static PyMethodDef dictvalues_methods[] = { {"__reversed__", (PyCFunction)dictvalues_reversed, METH_NOARGS, reversed_values_doc}, {NULL, NULL} /* sentinel */ }; static PyObject * dictvalues_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterValue_Type); } python-frozendict/src/frozendict/c_src/3_10/cpython_src/Objects/dictobject_original.c000066400000000000000000004515771470250572200313520ustar00rootroot00000000000000/* Dictionary object implementation using a hash table */ /* The distribution includes a separate file, Objects/dictnotes.txt, describing explorations into dictionary design and optimization. It covers typical dictionary use patterns, the parameters for tuning dictionaries, and several ideas for possible optimizations. */ /* PyDictKeysObject This implements the dictionary's hashtable. As of Python 3.6, this is compact and ordered. Basic idea is described here: * https://mail.python.org/pipermail/python-dev/2012-December/123028.html * https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html layout: +---------------+ | dk_refcnt | | dk_size | | dk_lookup | | dk_usable | | dk_nentries | +---------------+ | dk_indices | | | +---------------+ | dk_entries | | | +---------------+ dk_indices is actual hashtable. It holds index in entries, or DKIX_EMPTY(-1) or DKIX_DUMMY(-2). Size of indices is dk_size. Type of each index in indices is vary on dk_size: * int8 for dk_size <= 128 * int16 for 256 <= dk_size <= 2**15 * int32 for 2**16 <= dk_size <= 2**31 * int64 for 2**32 <= dk_size dk_entries is array of PyDictKeyEntry. Its size is USABLE_FRACTION(dk_size). DK_ENTRIES(dk) can be used to get pointer to entries. NOTE: Since negative value is used for DKIX_EMPTY and DKIX_DUMMY, type of dk_indices entry is signed integer and int16 is used for table which dk_size == 256. */ /* The DictObject can be in one of two forms. Either: A combined table: ma_values == NULL, dk_refcnt == 1. Values are stored in the me_value field of the PyDictKeysObject. Or: A split table: ma_values != NULL, dk_refcnt >= 1 Values are stored in the ma_values array. Only string (unicode) keys are allowed. All dicts sharing same key must have same insertion order. There are four kinds of slots in the table (slot is index, and DK_ENTRIES(keys)[index] if index >= 0): 1. Unused. index == DKIX_EMPTY Does not hold an active (key, value) pair now and never did. Unused can transition to Active upon key insertion. This is each slot's initial state. 2. Active. index >= 0, me_key != NULL and me_value != NULL Holds an active (key, value) pair. Active can transition to Dummy or Pending upon key deletion (for combined and split tables respectively). This is the only case in which me_value != NULL. 3. Dummy. index == DKIX_DUMMY (combined only) Previously held an active (key, value) pair, but that was deleted and an active pair has not yet overwritten the slot. Dummy can transition to Active upon key insertion. Dummy slots cannot be made Unused again else the probe sequence in case of collision would have no way to know they were once active. 4. Pending. index >= 0, key != NULL, and value == NULL (split only) Not yet inserted in split-table. */ /* Preserving insertion order It's simple for combined table. Since dk_entries is mostly append only, we can get insertion order by just iterating dk_entries. One exception is .popitem(). It removes last item in dk_entries and decrement dk_nentries to achieve amortized O(1). Since there are DKIX_DUMMY remains in dk_indices, we can't increment dk_usable even though dk_nentries is decremented. In split table, inserting into pending entry is allowed only for dk_entries[ix] where ix == mp->ma_used. Inserting into other index and deleting item cause converting the dict to the combined table. */ /* PyDict_MINSIZE is the starting size for any new dict. * 8 allows dicts with no more than 5 active entries; experiments suggested * this suffices for the majority of dicts (consisting mostly of usually-small * dicts created to pass keyword arguments). * Making this 8, rather than 4 reduces the number of resizes for most * dictionaries, without any significant extra memory use. */ #define PyDict_MINSIZE 8 #include "Python.h" #include "pycore_bitutils.h" // _Py_bit_length #include "pycore_gc.h" // _PyObject_GC_IS_TRACKED() #include "pycore_object.h" // _PyObject_GC_TRACK() #include "pycore_pyerrors.h" // _PyErr_Fetch() #include "pycore_pystate.h" // _PyThreadState_GET() #include "dict-common.h" #include "stringlib/eq.h" // unicode_eq() /*[clinic input] class dict "PyDictObject *" "&PyDict_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=f157a5a0ce9589d6]*/ /* To ensure the lookup algorithm terminates, there must be at least one Unused slot (NULL key) in the table. To avoid slowing down lookups on a near-full table, we resize the table when it's USABLE_FRACTION (currently two-thirds) full. */ #define PERTURB_SHIFT 5 /* Major subtleties ahead: Most hash schemes depend on having a "good" hash function, in the sense of simulating randomness. Python doesn't: its most important hash functions (for ints) are very regular in common cases: >>>[hash(i) for i in range(4)] [0, 1, 2, 3] This isn't necessarily bad! To the contrary, in a table of size 2**i, taking the low-order i bits as the initial table index is extremely fast, and there are no collisions at all for dicts indexed by a contiguous range of ints. So this gives better-than-random behavior in common cases, and that's very desirable. OTOH, when collisions occur, the tendency to fill contiguous slices of the hash table makes a good collision resolution strategy crucial. Taking only the last i bits of the hash code is also vulnerable: for example, consider the list [i << 16 for i in range(20000)] as a set of keys. Since ints are their own hash codes, and this fits in a dict of size 2**15, the last 15 bits of every hash code are all 0: they *all* map to the same table index. But catering to unusual cases should not slow the usual ones, so we just take the last i bits anyway. It's up to collision resolution to do the rest. If we *usually* find the key we're looking for on the first try (and, it turns out, we usually do -- the table load factor is kept under 2/3, so the odds are solidly in our favor), then it makes best sense to keep the initial index computation dirt cheap. The first half of collision resolution is to visit table indices via this recurrence: j = ((5*j) + 1) mod 2**i For any initial j in range(2**i), repeating that 2**i times generates each int in range(2**i) exactly once (see any text on random-number generation for proof). By itself, this doesn't help much: like linear probing (setting j += 1, or j -= 1, on each loop trip), it scans the table entries in a fixed order. This would be bad, except that's not the only thing we do, and it's actually *good* in the common cases where hash keys are consecutive. In an example that's really too small to make this entirely clear, for a table of size 2**3 the order of indices is: 0 -> 1 -> 6 -> 7 -> 4 -> 5 -> 2 -> 3 -> 0 [and here it's repeating] If two things come in at index 5, the first place we look after is index 2, not 6, so if another comes in at index 6 the collision at 5 didn't hurt it. Linear probing is deadly in this case because there the fixed probe order is the *same* as the order consecutive keys are likely to arrive. But it's extremely unlikely hash codes will follow a 5*j+1 recurrence by accident, and certain that consecutive hash codes do not. The other half of the strategy is to get the other bits of the hash code into play. This is done by initializing a (unsigned) vrbl "perturb" to the full hash code, and changing the recurrence to: perturb >>= PERTURB_SHIFT; j = (5*j) + 1 + perturb; use j % 2**i as the next table index; Now the probe sequence depends (eventually) on every bit in the hash code, and the pseudo-scrambling property of recurring on 5*j+1 is more valuable, because it quickly magnifies small differences in the bits that didn't affect the initial index. Note that because perturb is unsigned, if the recurrence is executed often enough perturb eventually becomes and remains 0. At that point (very rarely reached) the recurrence is on (just) 5*j+1 again, and that's certain to find an empty slot eventually (since it generates every int in range(2**i), and we make sure there's always at least one empty slot). Selecting a good value for PERTURB_SHIFT is a balancing act. You want it small so that the high bits of the hash code continue to affect the probe sequence across iterations; but you want it large so that in really bad cases the high-order hash bits have an effect on early iterations. 5 was "the best" in minimizing total collisions across experiments Tim Peters ran (on both normal and pathological cases), but 4 and 6 weren't significantly worse. Historical: Reimer Behrends contributed the idea of using a polynomial-based approach, using repeated multiplication by x in GF(2**n) where an irreducible polynomial for each table size was chosen such that x was a primitive root. Christian Tismer later extended that to use division by x instead, as an efficient way to get the high bits of the hash code into play. This scheme also gave excellent collision statistics, but was more expensive: two if-tests were required inside the loop; computing "the next" index took about the same number of operations but without as much potential parallelism (e.g., computing 5*j can go on at the same time as computing 1+perturb in the above, and then shifting perturb can be done while the table index is being masked); and the PyDictObject struct required a member to hold the table's polynomial. In Tim's experiments the current scheme ran faster, produced equally good collision statistics, needed less code & used less memory. */ /* forward declarations */ static Py_ssize_t lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static Py_ssize_t lookdict_unicode(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static Py_ssize_t lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static Py_ssize_t lookdict_split(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static int dictresize(PyDictObject *mp, Py_ssize_t newsize); static PyObject* dict_iter(PyDictObject *dict); /*Global counter used to set ma_version_tag field of dictionary. * It is incremented each time that a dictionary is created and each * time that a dictionary is modified. */ static uint64_t pydict_global_version = 0; #define DICT_NEXT_VERSION() (++pydict_global_version) #include "clinic/dictobject.c.h" static struct _Py_dict_state * get_dict_state(void) { PyInterpreterState *interp = _PyInterpreterState_GET(); return &interp->dict_state; } void _PyDict_ClearFreeList(PyInterpreterState *interp) { struct _Py_dict_state *state = &interp->dict_state; while (state->numfree) { PyDictObject *op = state->free_list[--state->numfree]; assert(PyDict_CheckExact(op)); PyObject_GC_Del(op); } while (state->keys_numfree) { PyObject_Free(state->keys_free_list[--state->keys_numfree]); } } void _PyDict_Fini(PyInterpreterState *interp) { _PyDict_ClearFreeList(interp); #ifdef Py_DEBUG struct _Py_dict_state *state = &interp->dict_state; state->numfree = -1; state->keys_numfree = -1; #endif } /* Print summary info about the state of the optimized allocator */ void _PyDict_DebugMallocStats(FILE *out) { struct _Py_dict_state *state = get_dict_state(); _PyDebugAllocatorStats(out, "free PyDictObject", state->numfree, sizeof(PyDictObject)); } #define DK_SIZE(dk) ((dk)->dk_size) #if SIZEOF_VOID_P > 4 #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : DK_SIZE(dk) <= 0xffffffff ? \ 4 : sizeof(int64_t)) #else #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : sizeof(int32_t)) #endif #define DK_ENTRIES(dk) \ ((PyDictKeyEntry*)(&((int8_t*)((dk)->dk_indices))[DK_SIZE(dk) * DK_IXSIZE(dk)])) #define DK_MASK(dk) (((dk)->dk_size)-1) #define IS_POWER_OF_2(x) (((x) & (x-1)) == 0) static void free_keys_object(PyDictKeysObject *keys); static inline void dictkeys_incref(PyDictKeysObject *dk) { #ifdef Py_REF_DEBUG _Py_RefTotal++; #endif dk->dk_refcnt++; } static inline void dictkeys_decref(PyDictKeysObject *dk) { assert(dk->dk_refcnt > 0); #ifdef Py_REF_DEBUG _Py_RefTotal--; #endif if (--dk->dk_refcnt == 0) { free_keys_object(dk); } } /* lookup indices. returns DKIX_EMPTY, DKIX_DUMMY, or ix >=0 */ static inline Py_ssize_t dictkeys_get_index(const PyDictKeysObject *keys, Py_ssize_t i) { Py_ssize_t s = DK_SIZE(keys); Py_ssize_t ix; if (s <= 0xff) { const int8_t *indices = (const int8_t*)(keys->dk_indices); ix = indices[i]; } else if (s <= 0xffff) { const int16_t *indices = (const int16_t*)(keys->dk_indices); ix = indices[i]; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { const int64_t *indices = (const int64_t*)(keys->dk_indices); ix = indices[i]; } #endif else { const int32_t *indices = (const int32_t*)(keys->dk_indices); ix = indices[i]; } assert(ix >= DKIX_DUMMY); return ix; } /* write to indices. */ static inline void dictkeys_set_index(PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix) { Py_ssize_t s = DK_SIZE(keys); assert(ix >= DKIX_DUMMY); if (s <= 0xff) { int8_t *indices = (int8_t*)(keys->dk_indices); assert(ix <= 0x7f); indices[i] = (char)ix; } else if (s <= 0xffff) { int16_t *indices = (int16_t*)(keys->dk_indices); assert(ix <= 0x7fff); indices[i] = (int16_t)ix; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { int64_t *indices = (int64_t*)(keys->dk_indices); indices[i] = ix; } #endif else { int32_t *indices = (int32_t*)(keys->dk_indices); assert(ix <= 0x7fffffff); indices[i] = (int32_t)ix; } } /* USABLE_FRACTION is the maximum dictionary load. * Increasing this ratio makes dictionaries more dense resulting in more * collisions. Decreasing it improves sparseness at the expense of spreading * indices over more cache lines and at the cost of total memory consumed. * * USABLE_FRACTION must obey the following: * (0 < USABLE_FRACTION(n) < n) for all n >= 2 * * USABLE_FRACTION should be quick to calculate. * Fractions around 1/2 to 2/3 seem to work well in practice. */ #define USABLE_FRACTION(n) (((n) << 1)/3) /* Find the smallest dk_size >= minsize. */ static inline Py_ssize_t calculate_keysize(Py_ssize_t minsize) { #if SIZEOF_LONG == SIZEOF_SIZE_T minsize = (minsize | PyDict_MINSIZE) - 1; return 1LL << _Py_bit_length(minsize | (PyDict_MINSIZE-1)); #elif defined(_MSC_VER) // On 64bit Windows, sizeof(long) == 4. minsize = (minsize | PyDict_MINSIZE) - 1; unsigned long msb; _BitScanReverse64(&msb, (uint64_t)minsize); return 1LL << (msb + 1); #else Py_ssize_t size; for (size = PyDict_MINSIZE; size < minsize && size > 0; size <<= 1) ; return size; #endif } /* estimate_keysize is reverse function of USABLE_FRACTION. * * This can be used to reserve enough size to insert n entries without * resizing. */ static inline Py_ssize_t estimate_keysize(Py_ssize_t n) { return calculate_keysize((n*3 + 1) / 2); } /* GROWTH_RATE. Growth rate upon hitting maximum load. * Currently set to used*3. * This means that dicts double in size when growing without deletions, * but have more head room when the number of deletions is on a par with the * number of insertions. See also bpo-17563 and bpo-33205. * * GROWTH_RATE was set to used*4 up to version 3.2. * GROWTH_RATE was set to used*2 in version 3.3.0 * GROWTH_RATE was set to used*2 + capacity/2 in 3.4.0-3.6.0. */ #define GROWTH_RATE(d) ((d)->ma_used*3) #define ENSURE_ALLOWS_DELETIONS(d) \ if ((d)->ma_keys->dk_lookup == lookdict_unicode_nodummy) { \ (d)->ma_keys->dk_lookup = lookdict_unicode; \ } /* This immutable, empty PyDictKeysObject is used for PyDict_Clear() * (which cannot fail and thus can do no allocation). */ static PyDictKeysObject empty_keys_struct = { 1, /* dk_refcnt */ 1, /* dk_size */ lookdict_split, /* dk_lookup */ 0, /* dk_usable (immutable) */ 0, /* dk_nentries */ {DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY}, /* dk_indices */ }; static PyObject *empty_values[1] = { NULL }; #define Py_EMPTY_KEYS &empty_keys_struct /* Uncomment to check the dict content in _PyDict_CheckConsistency() */ /* #define DEBUG_PYDICT */ #ifdef DEBUG_PYDICT # define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 1)) #else # define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 0)) #endif int _PyDict_CheckConsistency(PyObject *op, int check_content) { #define CHECK(expr) \ do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0) assert(op != NULL); CHECK(PyDict_Check(op)); PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; int splitted = _PyDict_HasSplitTable(mp); Py_ssize_t usable = USABLE_FRACTION(keys->dk_size); CHECK(0 <= mp->ma_used && mp->ma_used <= usable); CHECK(IS_POWER_OF_2(keys->dk_size)); CHECK(0 <= keys->dk_usable && keys->dk_usable <= usable); CHECK(0 <= keys->dk_nentries && keys->dk_nentries <= usable); CHECK(keys->dk_usable + keys->dk_nentries <= usable); if (!splitted) { /* combined table */ CHECK(keys->dk_refcnt == 1); } if (check_content) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i; for (i=0; i < keys->dk_size; i++) { Py_ssize_t ix = dictkeys_get_index(keys, i); CHECK(DKIX_DUMMY <= ix && ix <= usable); } for (i=0; i < usable; i++) { PyDictKeyEntry *entry = &entries[i]; PyObject *key = entry->me_key; if (key != NULL) { if (PyUnicode_CheckExact(key)) { Py_hash_t hash = ((PyASCIIObject *)key)->hash; CHECK(hash != -1); CHECK(entry->me_hash == hash); } else { /* test_dict fails if PyObject_Hash() is called again */ CHECK(entry->me_hash != -1); } if (!splitted) { CHECK(entry->me_value != NULL); } } if (splitted) { CHECK(entry->me_value == NULL); } } if (splitted) { /* splitted table */ for (i=0; i < mp->ma_used; i++) { CHECK(mp->ma_values[i] != NULL); } } } return 1; #undef CHECK } static PyDictKeysObject* new_keys_object(Py_ssize_t size) { PyDictKeysObject *dk; Py_ssize_t es, usable; assert(size >= PyDict_MINSIZE); assert(IS_POWER_OF_2(size)); usable = USABLE_FRACTION(size); if (size <= 0xff) { es = 1; } else if (size <= 0xffff) { es = 2; } #if SIZEOF_VOID_P > 4 else if (size <= 0xffffffff) { es = 4; } #endif else { es = sizeof(Py_ssize_t); } struct _Py_dict_state *state = get_dict_state(); #ifdef Py_DEBUG // new_keys_object() must not be called after _PyDict_Fini() assert(state->keys_numfree != -1); #endif if (size == PyDict_MINSIZE && state->keys_numfree > 0) { dk = state->keys_free_list[--state->keys_numfree]; } else { dk = PyObject_Malloc(sizeof(PyDictKeysObject) + es * size + sizeof(PyDictKeyEntry) * usable); if (dk == NULL) { PyErr_NoMemory(); return NULL; } } #ifdef Py_REF_DEBUG _Py_RefTotal++; #endif dk->dk_refcnt = 1; dk->dk_size = size; dk->dk_usable = usable; dk->dk_lookup = lookdict_unicode_nodummy; dk->dk_nentries = 0; memset(&dk->dk_indices[0], 0xff, es * size); memset(DK_ENTRIES(dk), 0, sizeof(PyDictKeyEntry) * usable); return dk; } static void free_keys_object(PyDictKeysObject *keys) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n; for (i = 0, n = keys->dk_nentries; i < n; i++) { Py_XDECREF(entries[i].me_key); Py_XDECREF(entries[i].me_value); } struct _Py_dict_state *state = get_dict_state(); #ifdef Py_DEBUG // free_keys_object() must not be called after _PyDict_Fini() assert(state->keys_numfree != -1); #endif if (keys->dk_size == PyDict_MINSIZE && state->keys_numfree < PyDict_MAXFREELIST) { state->keys_free_list[state->keys_numfree++] = keys; return; } PyObject_Free(keys); } #define new_values(size) PyMem_NEW(PyObject *, size) #define free_values(values) PyMem_Free(values) /* Consumes a reference to the keys object */ static PyObject * new_dict(PyDictKeysObject *keys, PyObject **values) { PyDictObject *mp; assert(keys != NULL); struct _Py_dict_state *state = get_dict_state(); #ifdef Py_DEBUG // new_dict() must not be called after _PyDict_Fini() assert(state->numfree != -1); #endif if (state->numfree) { mp = state->free_list[--state->numfree]; assert (mp != NULL); assert (Py_IS_TYPE(mp, &PyDict_Type)); _Py_NewReference((PyObject *)mp); } else { mp = PyObject_GC_New(PyDictObject, &PyDict_Type); if (mp == NULL) { dictkeys_decref(keys); if (values != empty_values) { free_values(values); } return NULL; } } mp->ma_keys = keys; mp->ma_values = values; mp->ma_used = 0; mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(mp); return (PyObject *)mp; } /* Consumes a reference to the keys object */ static PyObject * new_dict_with_shared_keys(PyDictKeysObject *keys) { PyObject **values; Py_ssize_t i, size; size = USABLE_FRACTION(DK_SIZE(keys)); values = new_values(size); if (values == NULL) { dictkeys_decref(keys); return PyErr_NoMemory(); } for (i = 0; i < size; i++) { values[i] = NULL; } return new_dict(keys, values); } static PyDictKeysObject * clone_combined_dict_keys(PyDictObject *orig) { assert(PyDict_Check(orig)); assert(Py_TYPE(orig)->tp_iter == (getiterfunc)dict_iter); assert(orig->ma_values == NULL); assert(orig->ma_keys->dk_refcnt == 1); Py_ssize_t keys_size = _PyDict_KeysSize(orig->ma_keys); PyDictKeysObject *keys = PyObject_Malloc(keys_size); if (keys == NULL) { PyErr_NoMemory(); return NULL; } memcpy(keys, orig->ma_keys, keys_size); /* After copying key/value pairs, we need to incref all keys and values and they are about to be co-owned by a new dict object. */ PyDictKeyEntry *ep0 = DK_ENTRIES(keys); Py_ssize_t n = keys->dk_nentries; for (Py_ssize_t i = 0; i < n; i++) { PyDictKeyEntry *entry = &ep0[i]; PyObject *value = entry->me_value; if (value != NULL) { Py_INCREF(value); Py_INCREF(entry->me_key); } } /* Since we copied the keys table we now have an extra reference in the system. Manually call increment _Py_RefTotal to signal that we have it now; calling dictkeys_incref would be an error as keys->dk_refcnt is already set to 1 (after memcpy). */ #ifdef Py_REF_DEBUG _Py_RefTotal++; #endif return keys; } PyObject * PyDict_New(void) { dictkeys_incref(Py_EMPTY_KEYS); return new_dict(Py_EMPTY_KEYS, empty_values); } /* Search index of hash table from offset of entry table */ static Py_ssize_t lookdict_index(PyDictKeysObject *k, Py_hash_t hash, Py_ssize_t index) { size_t mask = DK_MASK(k); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(k, i); if (ix == index) { return i; } if (ix == DKIX_EMPTY) { return DKIX_EMPTY; } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } /* The basic lookup function used by all operations. This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4. Open addressing is preferred over chaining since the link overhead for chaining would be substantial (100% with typical malloc overhead). The initial probe index is computed as hash mod the table size. Subsequent probe indices are computed as explained earlier. All arithmetic on hash should ignore overflow. The details in this version are due to Tim Peters, building on many past contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and Christian Tismer. lookdict() is general-purpose, and may return DKIX_ERROR if (and only if) a comparison raises an exception. lookdict_unicode() below is specialized to string keys, comparison of which can never raise an exception; that function can never return DKIX_ERROR when key is string. Otherwise, it falls back to lookdict(). lookdict_unicode_nodummy is further specialized for string keys that cannot be the value. For both, when the key isn't found a DKIX_EMPTY is returned. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { size_t i, mask, perturb; PyDictKeysObject *dk; PyDictKeyEntry *ep0; top: dk = mp->ma_keys; ep0 = DK_ENTRIES(dk); mask = DK_MASK(dk); perturb = hash; i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(dk, i); if (ix == DKIX_EMPTY) { *value_addr = NULL; return ix; } if (ix >= 0) { PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); if (ep->me_key == key) { *value_addr = ep->me_value; return ix; } if (ep->me_hash == hash) { PyObject *startkey = ep->me_key; Py_INCREF(startkey); int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); Py_DECREF(startkey); if (cmp < 0) { *value_addr = NULL; return DKIX_ERROR; } if (dk == mp->ma_keys && ep->me_key == startkey) { if (cmp > 0) { *value_addr = ep->me_value; return ix; } } else { /* The dict was mutated, restart */ goto top; } } } perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; } Py_UNREACHABLE(); } /* Specialized version for string-only keys */ static Py_ssize_t _Py_HOT_FUNCTION lookdict_unicode(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { assert(mp->ma_values == NULL); /* Make sure this function doesn't have to handle non-unicode keys, including subclasses of str; e.g., one reason to subclass unicodes is to override __eq__, and for speed we don't cater to that here. */ if (!PyUnicode_CheckExact(key)) { return lookdict(mp, key, hash, value_addr); } PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); size_t mask = DK_MASK(mp->ma_keys); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i); if (ix == DKIX_EMPTY) { *value_addr = NULL; return DKIX_EMPTY; } if (ix >= 0) { PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { *value_addr = ep->me_value; return ix; } } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } /* Faster version of lookdict_unicode when it is known that no keys * will be present. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { assert(mp->ma_values == NULL); /* Make sure this function doesn't have to handle non-unicode keys, including subclasses of str; e.g., one reason to subclass unicodes is to override __eq__, and for speed we don't cater to that here. */ if (!PyUnicode_CheckExact(key)) { return lookdict(mp, key, hash, value_addr); } PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); size_t mask = DK_MASK(mp->ma_keys); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i); assert (ix != DKIX_DUMMY); if (ix == DKIX_EMPTY) { *value_addr = NULL; return DKIX_EMPTY; } PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { *value_addr = ep->me_value; return ix; } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } /* Version of lookdict for split tables. * All split tables and only split tables use this lookup function. * Split tables only contain unicode keys and no dummy keys, * so algorithm is the same as lookdict_unicode_nodummy. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict_split(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { /* mp must split table */ assert(mp->ma_values != NULL); if (!PyUnicode_CheckExact(key)) { Py_ssize_t ix = lookdict(mp, key, hash, value_addr); if (ix >= 0) { *value_addr = mp->ma_values[ix]; } return ix; } PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); size_t mask = DK_MASK(mp->ma_keys); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i); assert (ix != DKIX_DUMMY); if (ix == DKIX_EMPTY) { *value_addr = NULL; return DKIX_EMPTY; } PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { *value_addr = mp->ma_values[ix]; return ix; } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } int _PyDict_HasOnlyStringKeys(PyObject *dict) { Py_ssize_t pos = 0; PyObject *key, *value; assert(PyDict_Check(dict)); /* Shortcut */ if (((PyDictObject *)dict)->ma_keys->dk_lookup != lookdict) return 1; while (PyDict_Next(dict, &pos, &key, &value)) if (!PyUnicode_Check(key)) return 0; return 1; } #define MAINTAIN_TRACKING(mp, key, value) \ do { \ if (!_PyObject_GC_IS_TRACKED(mp)) { \ if (_PyObject_GC_MAY_BE_TRACKED(key) || \ _PyObject_GC_MAY_BE_TRACKED(value)) { \ _PyObject_GC_TRACK(mp); \ } \ } \ } while(0) void _PyDict_MaybeUntrack(PyObject *op) { PyDictObject *mp; PyObject *value; Py_ssize_t i, numentries; PyDictKeyEntry *ep0; if (!PyDict_CheckExact(op) || !_PyObject_GC_IS_TRACKED(op)) return; mp = (PyDictObject *) op; ep0 = DK_ENTRIES(mp->ma_keys); numentries = mp->ma_keys->dk_nentries; if (_PyDict_HasSplitTable(mp)) { for (i = 0; i < numentries; i++) { if ((value = mp->ma_values[i]) == NULL) continue; if (_PyObject_GC_MAY_BE_TRACKED(value)) { assert(!_PyObject_GC_MAY_BE_TRACKED(ep0[i].me_key)); return; } } } else { for (i = 0; i < numentries; i++) { if ((value = ep0[i].me_value) == NULL) continue; if (_PyObject_GC_MAY_BE_TRACKED(value) || _PyObject_GC_MAY_BE_TRACKED(ep0[i].me_key)) return; } } _PyObject_GC_UNTRACK(op); } /* Internal function to find slot for an item from its hash when it is known that the key is not present in the dict. The dict must be combined. */ static Py_ssize_t find_empty_slot(PyDictKeysObject *keys, Py_hash_t hash) { assert(keys != NULL); const size_t mask = DK_MASK(keys); size_t i = hash & mask; Py_ssize_t ix = dictkeys_get_index(keys, i); for (size_t perturb = hash; ix >= 0;) { perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; ix = dictkeys_get_index(keys, i); } return i; } static int insertion_resize(PyDictObject *mp) { return dictresize(mp, calculate_keysize(GROWTH_RATE(mp))); } /* Internal routine to insert a new item into the table. Used both by the internal resize routine and by the public insert routine. Returns -1 if an error occurred, or 0 on success. */ static int insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) { PyObject *old_value; PyDictKeyEntry *ep; Py_INCREF(key); Py_INCREF(value); if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) { if (insertion_resize(mp) < 0) goto Fail; } Py_ssize_t ix = mp->ma_keys->dk_lookup(mp, key, hash, &old_value); if (ix == DKIX_ERROR) goto Fail; MAINTAIN_TRACKING(mp, key, value); /* When insertion order is different from shared key, we can't share * the key anymore. Convert this instance to combine table. */ if (_PyDict_HasSplitTable(mp) && ((ix >= 0 && old_value == NULL && mp->ma_used != ix) || (ix == DKIX_EMPTY && mp->ma_used != mp->ma_keys->dk_nentries))) { if (insertion_resize(mp) < 0) goto Fail; ix = DKIX_EMPTY; } if (ix == DKIX_EMPTY) { /* Insert into new slot. */ assert(old_value == NULL); if (mp->ma_keys->dk_usable <= 0) { /* Need to resize. */ if (insertion_resize(mp) < 0) goto Fail; } if (!PyUnicode_CheckExact(key) && mp->ma_keys->dk_lookup != lookdict) { mp->ma_keys->dk_lookup = lookdict; } Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash); ep = &DK_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries]; dictkeys_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries); ep->me_key = key; ep->me_hash = hash; if (mp->ma_values) { assert (mp->ma_values[mp->ma_keys->dk_nentries] == NULL); mp->ma_values[mp->ma_keys->dk_nentries] = value; } else { ep->me_value = value; } mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); mp->ma_keys->dk_usable--; mp->ma_keys->dk_nentries++; assert(mp->ma_keys->dk_usable >= 0); ASSERT_CONSISTENT(mp); return 0; } if (old_value != value) { if (_PyDict_HasSplitTable(mp)) { mp->ma_values[ix] = value; if (old_value == NULL) { /* pending state */ assert(ix == mp->ma_used); mp->ma_used++; } } else { assert(old_value != NULL); DK_ENTRIES(mp->ma_keys)[ix].me_value = value; } mp->ma_version_tag = DICT_NEXT_VERSION(); } Py_XDECREF(old_value); /* which **CAN** re-enter (see issue #22653) */ ASSERT_CONSISTENT(mp); Py_DECREF(key); return 0; Fail: Py_DECREF(value); Py_DECREF(key); return -1; } // Same to insertdict but specialized for ma_keys = Py_EMPTY_KEYS. static int insert_to_emptydict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) { assert(mp->ma_keys == Py_EMPTY_KEYS); PyDictKeysObject *newkeys = new_keys_object(PyDict_MINSIZE); if (newkeys == NULL) { return -1; } if (!PyUnicode_CheckExact(key)) { newkeys->dk_lookup = lookdict; } dictkeys_decref(Py_EMPTY_KEYS); mp->ma_keys = newkeys; mp->ma_values = NULL; Py_INCREF(key); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); size_t hashpos = (size_t)hash & (PyDict_MINSIZE-1); PyDictKeyEntry *ep = DK_ENTRIES(mp->ma_keys); dictkeys_set_index(mp->ma_keys, hashpos, 0); ep->me_key = key; ep->me_hash = hash; ep->me_value = value; mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); mp->ma_keys->dk_usable--; mp->ma_keys->dk_nentries++; return 0; } /* Internal routine used by dictresize() to build a hashtable of entries. */ static void build_indices(PyDictKeysObject *keys, PyDictKeyEntry *ep, Py_ssize_t n) { size_t mask = (size_t)DK_SIZE(keys) - 1; for (Py_ssize_t ix = 0; ix != n; ix++, ep++) { Py_hash_t hash = ep->me_hash; size_t i = hash & mask; for (size_t perturb = hash; dictkeys_get_index(keys, i) != DKIX_EMPTY;) { perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } dictkeys_set_index(keys, i, ix); } } /* Restructure the table by allocating a new table and reinserting all items again. When entries have been deleted, the new table may actually be smaller than the old one. If a table is split (its keys and hashes are shared, its values are not), then the values are temporarily copied into the table, it is resized as a combined table, then the me_value slots in the old table are NULLed out. After resizing a table is always combined, but can be resplit by make_keys_shared(). */ static int dictresize(PyDictObject *mp, Py_ssize_t newsize) { Py_ssize_t numentries; PyDictKeysObject *oldkeys; PyObject **oldvalues; PyDictKeyEntry *oldentries, *newentries; if (newsize <= 0) { PyErr_NoMemory(); return -1; } assert(IS_POWER_OF_2(newsize)); assert(newsize >= PyDict_MINSIZE); oldkeys = mp->ma_keys; /* NOTE: Current odict checks mp->ma_keys to detect resize happen. * So we can't reuse oldkeys even if oldkeys->dk_size == newsize. * TODO: Try reusing oldkeys when reimplement odict. */ /* Allocate a new table. */ mp->ma_keys = new_keys_object(newsize); if (mp->ma_keys == NULL) { mp->ma_keys = oldkeys; return -1; } // New table must be large enough. assert(mp->ma_keys->dk_usable >= mp->ma_used); if (oldkeys->dk_lookup == lookdict) mp->ma_keys->dk_lookup = lookdict; numentries = mp->ma_used; oldentries = DK_ENTRIES(oldkeys); newentries = DK_ENTRIES(mp->ma_keys); oldvalues = mp->ma_values; if (oldvalues != NULL) { /* Convert split table into new combined table. * We must incref keys; we can transfer values. * Note that values of split table is always dense. */ for (Py_ssize_t i = 0; i < numentries; i++) { assert(oldvalues[i] != NULL); PyDictKeyEntry *ep = &oldentries[i]; PyObject *key = ep->me_key; Py_INCREF(key); newentries[i].me_key = key; newentries[i].me_hash = ep->me_hash; newentries[i].me_value = oldvalues[i]; } dictkeys_decref(oldkeys); mp->ma_values = NULL; if (oldvalues != empty_values) { free_values(oldvalues); } } else { // combined table. if (oldkeys->dk_nentries == numentries) { memcpy(newentries, oldentries, numentries * sizeof(PyDictKeyEntry)); } else { PyDictKeyEntry *ep = oldentries; for (Py_ssize_t i = 0; i < numentries; i++) { while (ep->me_value == NULL) ep++; newentries[i] = *ep++; } } assert(oldkeys->dk_lookup != lookdict_split); assert(oldkeys->dk_refcnt == 1); #ifdef Py_REF_DEBUG _Py_RefTotal--; #endif struct _Py_dict_state *state = get_dict_state(); #ifdef Py_DEBUG // dictresize() must not be called after _PyDict_Fini() assert(state->keys_numfree != -1); #endif if (oldkeys->dk_size == PyDict_MINSIZE && state->keys_numfree < PyDict_MAXFREELIST) { state->keys_free_list[state->keys_numfree++] = oldkeys; } else { PyObject_Free(oldkeys); } } build_indices(mp->ma_keys, newentries, numentries); mp->ma_keys->dk_usable -= numentries; mp->ma_keys->dk_nentries = numentries; return 0; } /* Returns NULL if unable to split table. * A NULL return does not necessarily indicate an error */ static PyDictKeysObject * make_keys_shared(PyObject *op) { Py_ssize_t i; Py_ssize_t size; PyDictObject *mp = (PyDictObject *)op; if (!PyDict_CheckExact(op)) return NULL; if (!_PyDict_HasSplitTable(mp)) { PyDictKeyEntry *ep0; PyObject **values; assert(mp->ma_keys->dk_refcnt == 1); if (mp->ma_keys->dk_lookup == lookdict) { return NULL; } else if (mp->ma_keys->dk_lookup == lookdict_unicode) { /* Remove dummy keys */ if (dictresize(mp, DK_SIZE(mp->ma_keys))) return NULL; } assert(mp->ma_keys->dk_lookup == lookdict_unicode_nodummy); /* Copy values into a new array */ ep0 = DK_ENTRIES(mp->ma_keys); size = USABLE_FRACTION(DK_SIZE(mp->ma_keys)); values = new_values(size); if (values == NULL) { PyErr_SetString(PyExc_MemoryError, "Not enough memory to allocate new values array"); return NULL; } for (i = 0; i < size; i++) { values[i] = ep0[i].me_value; ep0[i].me_value = NULL; } mp->ma_keys->dk_lookup = lookdict_split; mp->ma_values = values; } dictkeys_incref(mp->ma_keys); return mp->ma_keys; } PyObject * _PyDict_NewPresized(Py_ssize_t minused) { const Py_ssize_t max_presize = 128 * 1024; Py_ssize_t newsize; PyDictKeysObject *new_keys; if (minused <= USABLE_FRACTION(PyDict_MINSIZE)) { return PyDict_New(); } /* There are no strict guarantee that returned dict can contain minused * items without resize. So we create medium size dict instead of very * large dict or MemoryError. */ if (minused > USABLE_FRACTION(max_presize)) { newsize = max_presize; } else { newsize = estimate_keysize(minused); } new_keys = new_keys_object(newsize); if (new_keys == NULL) return NULL; return new_dict(new_keys, NULL); } /* Note that, for historical reasons, PyDict_GetItem() suppresses all errors * that may occur (originally dicts supported only string keys, and exceptions * weren't possible). So, while the original intent was that a NULL return * meant the key wasn't present, in reality it can mean that, or that an error * (suppressed) occurred while computing the key's hash, or that some error * (suppressed) occurred when comparing keys in the dict's internal probe * sequence. A nasty example of the latter is when a Python-coded comparison * function hits a stack-depth error, which can cause this to return NULL * even if the key is present. */ PyObject * PyDict_GetItem(PyObject *op, PyObject *key) { if (!PyDict_Check(op)) { return NULL; } PyDictObject *mp = (PyDictObject *)op; Py_hash_t hash; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { PyErr_Clear(); return NULL; } } PyThreadState *tstate = _PyThreadState_GET(); #ifdef Py_DEBUG // bpo-40839: Before Python 3.10, it was possible to call PyDict_GetItem() // with the GIL released. _Py_EnsureTstateNotNULL(tstate); #endif /* Preserve the existing exception */ PyObject *exc_type, *exc_value, *exc_tb; PyObject *value; Py_ssize_t ix; _PyErr_Fetch(tstate, &exc_type, &exc_value, &exc_tb); ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); /* Ignore any exception raised by the lookup */ _PyErr_Restore(tstate, exc_type, exc_value, exc_tb); if (ix < 0) { return NULL; } return value; } Py_ssize_t _PyDict_GetItemHint(PyDictObject *mp, PyObject *key, Py_ssize_t hint, PyObject **value) { assert(*value == NULL); assert(PyDict_CheckExact((PyObject*)mp)); assert(PyUnicode_CheckExact(key)); if (hint >= 0 && hint < mp->ma_keys->dk_nentries) { PyObject *res = NULL; PyDictKeyEntry *ep = DK_ENTRIES(mp->ma_keys) + (size_t)hint; if (ep->me_key == key) { if (mp->ma_keys->dk_lookup == lookdict_split) { assert(mp->ma_values != NULL); res = mp->ma_values[(size_t)hint]; } else { res = ep->me_value; } if (res != NULL) { *value = res; return hint; } } } Py_hash_t hash = ((PyASCIIObject *) key)->hash; if (hash == -1) { hash = PyObject_Hash(key); if (hash == -1) { return -1; } } return (mp->ma_keys->dk_lookup)(mp, key, hash, value); } /* Same as PyDict_GetItemWithError() but with hash supplied by caller. This returns NULL *with* an exception set if an exception occurred. It returns NULL *without* an exception set if the key wasn't present. */ PyObject * _PyDict_GetItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) { Py_ssize_t ix; PyDictObject *mp = (PyDictObject *)op; PyObject *value; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix < 0) { return NULL; } return value; } /* Variant of PyDict_GetItem() that doesn't suppress exceptions. This returns NULL *with* an exception set if an exception occurred. It returns NULL *without* an exception set if the key wasn't present. */ PyObject * PyDict_GetItemWithError(PyObject *op, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyDictObject*mp = (PyDictObject *)op; PyObject *value; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { return NULL; } } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix < 0) return NULL; return value; } PyObject * _PyDict_GetItemIdWithError(PyObject *dp, struct _Py_Identifier *key) { PyObject *kv; kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) return NULL; Py_hash_t hash = ((PyASCIIObject *) kv)->hash; assert (hash != -1); /* interned strings have their hash value initialised */ return _PyDict_GetItem_KnownHash(dp, kv, hash); } PyObject * _PyDict_GetItemStringWithError(PyObject *v, const char *key) { PyObject *kv, *rv; kv = PyUnicode_FromString(key); if (kv == NULL) { return NULL; } rv = PyDict_GetItemWithError(v, kv); Py_DECREF(kv); return rv; } /* Fast version of global value lookup (LOAD_GLOBAL). * Lookup in globals, then builtins. * * Raise an exception and return NULL if an error occurred (ex: computing the * key hash failed, key comparison failed, ...). Return NULL if the key doesn't * exist. Return the value if the key exists. */ PyObject * _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } /* namespace 1: globals */ ix = globals->ma_keys->dk_lookup(globals, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix != DKIX_EMPTY && value != NULL) return value; /* namespace 2: builtins */ ix = builtins->ma_keys->dk_lookup(builtins, key, hash, &value); if (ix < 0) return NULL; return value; } /* CAUTION: PyDict_SetItem() must guarantee that it won't resize the * dictionary if it's merely replacing the value for an existing key. * This means that it's safe to loop over a dictionary with PyDict_Next() * and occasionally replace a value -- but you can't insert new keys or * remove them. */ int PyDict_SetItem(PyObject *op, PyObject *key, PyObject *value) { PyDictObject *mp; Py_hash_t hash; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); assert(value); mp = (PyDictObject *)op; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; } if (mp->ma_keys == Py_EMPTY_KEYS) { return insert_to_emptydict(mp, key, hash, value); } /* insertdict() handles any resizing that might be necessary */ return insertdict(mp, key, hash, value); } int _PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value, Py_hash_t hash) { PyDictObject *mp; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); assert(value); assert(hash != -1); mp = (PyDictObject *)op; if (mp->ma_keys == Py_EMPTY_KEYS) { return insert_to_emptydict(mp, key, hash, value); } /* insertdict() handles any resizing that might be necessary */ return insertdict(mp, key, hash, value); } static int delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix, PyObject *old_value) { PyObject *old_key; PyDictKeyEntry *ep; Py_ssize_t hashpos = lookdict_index(mp->ma_keys, hash, ix); assert(hashpos >= 0); mp->ma_used--; mp->ma_version_tag = DICT_NEXT_VERSION(); ep = &DK_ENTRIES(mp->ma_keys)[ix]; dictkeys_set_index(mp->ma_keys, hashpos, DKIX_DUMMY); ENSURE_ALLOWS_DELETIONS(mp); old_key = ep->me_key; ep->me_key = NULL; ep->me_value = NULL; Py_DECREF(old_key); Py_DECREF(old_value); ASSERT_CONSISTENT(mp); return 0; } int PyDict_DelItem(PyObject *op, PyObject *key) { Py_hash_t hash; assert(key); if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; } return _PyDict_DelItem_KnownHash(op, key, hash); } int _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) { Py_ssize_t ix; PyDictObject *mp; PyObject *old_value; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); assert(hash != -1); mp = (PyDictObject *)op; ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); if (ix == DKIX_ERROR) return -1; if (ix == DKIX_EMPTY || old_value == NULL) { _PyErr_SetKeyError(key); return -1; } // Split table doesn't allow deletion. Combine it. if (_PyDict_HasSplitTable(mp)) { if (dictresize(mp, DK_SIZE(mp->ma_keys))) { return -1; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); assert(ix >= 0); } return delitem_common(mp, hash, ix, old_value); } /* This function promises that the predicate -> deletion sequence is atomic * (i.e. protected by the GIL), assuming the predicate itself doesn't * release the GIL. */ int _PyDict_DelItemIf(PyObject *op, PyObject *key, int (*predicate)(PyObject *value)) { Py_ssize_t hashpos, ix; PyDictObject *mp; Py_hash_t hash; PyObject *old_value; int res; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); hash = PyObject_Hash(key); if (hash == -1) return -1; mp = (PyDictObject *)op; ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); if (ix == DKIX_ERROR) return -1; if (ix == DKIX_EMPTY || old_value == NULL) { _PyErr_SetKeyError(key); return -1; } // Split table doesn't allow deletion. Combine it. if (_PyDict_HasSplitTable(mp)) { if (dictresize(mp, DK_SIZE(mp->ma_keys))) { return -1; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); assert(ix >= 0); } res = predicate(old_value); if (res == -1) return -1; hashpos = lookdict_index(mp->ma_keys, hash, ix); assert(hashpos >= 0); if (res > 0) return delitem_common(mp, hashpos, ix, old_value); else return 0; } void PyDict_Clear(PyObject *op) { PyDictObject *mp; PyDictKeysObject *oldkeys; PyObject **oldvalues; Py_ssize_t i, n; if (!PyDict_Check(op)) return; mp = ((PyDictObject *)op); oldkeys = mp->ma_keys; oldvalues = mp->ma_values; if (oldvalues == empty_values) return; /* Empty the dict... */ dictkeys_incref(Py_EMPTY_KEYS); mp->ma_keys = Py_EMPTY_KEYS; mp->ma_values = empty_values; mp->ma_used = 0; mp->ma_version_tag = DICT_NEXT_VERSION(); /* ...then clear the keys and values */ if (oldvalues != NULL) { n = oldkeys->dk_nentries; for (i = 0; i < n; i++) Py_CLEAR(oldvalues[i]); free_values(oldvalues); dictkeys_decref(oldkeys); } else { assert(oldkeys->dk_refcnt == 1); dictkeys_decref(oldkeys); } ASSERT_CONSISTENT(mp); } /* Internal version of PyDict_Next that returns a hash value in addition * to the key and value. * Return 1 on success, return 0 when the reached the end of the dictionary * (or if op is not a dictionary) */ int _PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue, Py_hash_t *phash) { Py_ssize_t i; PyDictObject *mp; PyDictKeyEntry *entry_ptr; PyObject *value; if (!PyDict_Check(op)) return 0; mp = (PyDictObject *)op; i = *ppos; if (mp->ma_values) { if (i < 0 || i >= mp->ma_used) return 0; /* values of split table is always dense */ entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; value = mp->ma_values[i]; assert(value != NULL); } else { Py_ssize_t n = mp->ma_keys->dk_nentries; if (i < 0 || i >= n) return 0; entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) return 0; value = entry_ptr->me_value; } *ppos = i+1; if (pkey) *pkey = entry_ptr->me_key; if (phash) *phash = entry_ptr->me_hash; if (pvalue) *pvalue = value; return 1; } /* * Iterate over a dict. Use like so: * * Py_ssize_t i; * PyObject *key, *value; * i = 0; # important! i should not otherwise be changed by you * while (PyDict_Next(yourdict, &i, &key, &value)) { * Refer to borrowed references in key and value. * } * * Return 1 on success, return 0 when the reached the end of the dictionary * (or if op is not a dictionary) * * CAUTION: In general, it isn't safe to use PyDict_Next in a loop that * mutates the dict. One exception: it is safe if the loop merely changes * the values associated with the keys (but doesn't insert new keys or * delete keys), via PyDict_SetItem(). */ int PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) { return _PyDict_Next(op, ppos, pkey, pvalue, NULL); } /* Internal version of dict.pop(). */ PyObject * _PyDict_Pop_KnownHash(PyObject *dict, PyObject *key, Py_hash_t hash, PyObject *deflt) { Py_ssize_t ix, hashpos; PyObject *old_value, *old_key; PyDictKeyEntry *ep; PyDictObject *mp; assert(PyDict_Check(dict)); mp = (PyDictObject *)dict; if (mp->ma_used == 0) { if (deflt) { Py_INCREF(deflt); return deflt; } _PyErr_SetKeyError(key); return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || old_value == NULL) { if (deflt) { Py_INCREF(deflt); return deflt; } _PyErr_SetKeyError(key); return NULL; } // Split table doesn't allow deletion. Combine it. if (_PyDict_HasSplitTable(mp)) { if (dictresize(mp, DK_SIZE(mp->ma_keys))) { return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); assert(ix >= 0); } hashpos = lookdict_index(mp->ma_keys, hash, ix); assert(hashpos >= 0); assert(old_value != NULL); mp->ma_used--; mp->ma_version_tag = DICT_NEXT_VERSION(); dictkeys_set_index(mp->ma_keys, hashpos, DKIX_DUMMY); ep = &DK_ENTRIES(mp->ma_keys)[ix]; ENSURE_ALLOWS_DELETIONS(mp); old_key = ep->me_key; ep->me_key = NULL; ep->me_value = NULL; Py_DECREF(old_key); ASSERT_CONSISTENT(mp); return old_value; } PyObject * _PyDict_Pop(PyObject *dict, PyObject *key, PyObject *deflt) { Py_hash_t hash; if (((PyDictObject *)dict)->ma_used == 0) { if (deflt) { Py_INCREF(deflt); return deflt; } _PyErr_SetKeyError(key); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } return _PyDict_Pop_KnownHash(dict, key, hash, deflt); } /* Internal version of dict.from_keys(). It is subclass-friendly. */ PyObject * _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) { PyObject *it; /* iter(iterable) */ PyObject *key; PyObject *d; int status; d = _PyObject_CallNoArg(cls); if (d == NULL) return NULL; if (PyDict_CheckExact(d) && ((PyDictObject *)d)->ma_used == 0) { if (PyDict_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; PyObject *oldvalue; Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; if (dictresize(mp, estimate_keysize(PyDict_GET_SIZE(iterable)))) { Py_DECREF(d); return NULL; } while (_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) { if (insertdict(mp, key, hash, value)) { Py_DECREF(d); return NULL; } } return d; } if (PyAnySet_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; if (dictresize(mp, estimate_keysize(PySet_GET_SIZE(iterable)))) { Py_DECREF(d); return NULL; } while (_PySet_NextEntry(iterable, &pos, &key, &hash)) { if (insertdict(mp, key, hash, value)) { Py_DECREF(d); return NULL; } } return d; } } it = PyObject_GetIter(iterable); if (it == NULL){ Py_DECREF(d); return NULL; } if (PyDict_CheckExact(d)) { while ((key = PyIter_Next(it)) != NULL) { status = PyDict_SetItem(d, key, value); Py_DECREF(key); if (status < 0) goto Fail; } } else { while ((key = PyIter_Next(it)) != NULL) { status = PyObject_SetItem(d, key, value); Py_DECREF(key); if (status < 0) goto Fail; } } if (PyErr_Occurred()) goto Fail; Py_DECREF(it); return d; Fail: Py_DECREF(it); Py_DECREF(d); return NULL; } /* Methods */ static void dict_dealloc(PyDictObject *mp) { PyObject **values = mp->ma_values; PyDictKeysObject *keys = mp->ma_keys; Py_ssize_t i, n; /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(mp); Py_TRASHCAN_BEGIN(mp, dict_dealloc) if (values != NULL) { if (values != empty_values) { for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) { Py_XDECREF(values[i]); } free_values(values); } dictkeys_decref(keys); } else if (keys != NULL) { assert(keys->dk_refcnt == 1); dictkeys_decref(keys); } struct _Py_dict_state *state = get_dict_state(); #ifdef Py_DEBUG // new_dict() must not be called after _PyDict_Fini() assert(state->numfree != -1); #endif if (state->numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type)) { state->free_list[state->numfree++] = mp; } else { Py_TYPE(mp)->tp_free((PyObject *)mp); } Py_TRASHCAN_END } static PyObject * dict_repr(PyDictObject *mp) { Py_ssize_t i; PyObject *key = NULL, *value = NULL; _PyUnicodeWriter writer; int first; i = Py_ReprEnter((PyObject *)mp); if (i != 0) { return i > 0 ? PyUnicode_FromString("{...}") : NULL; } if (mp->ma_used == 0) { Py_ReprLeave((PyObject *)mp); return PyUnicode_FromString("{}"); } _PyUnicodeWriter_Init(&writer); writer.overallocate = 1; /* "{" + "1: 2" + ", 3: 4" * (len - 1) + "}" */ writer.min_length = 1 + 4 + (2 + 4) * (mp->ma_used - 1) + 1; if (_PyUnicodeWriter_WriteChar(&writer, '{') < 0) goto error; /* Do repr() on each key+value pair, and insert ": " between them. Note that repr may mutate the dict. */ i = 0; first = 1; while (PyDict_Next((PyObject *)mp, &i, &key, &value)) { PyObject *s; int res; /* Prevent repr from deleting key or value during key format. */ Py_INCREF(key); Py_INCREF(value); if (!first) { if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0) goto error; } first = 0; s = PyObject_Repr(key); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; if (_PyUnicodeWriter_WriteASCIIString(&writer, ": ", 2) < 0) goto error; s = PyObject_Repr(value); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; Py_CLEAR(key); Py_CLEAR(value); } writer.overallocate = 0; if (_PyUnicodeWriter_WriteChar(&writer, '}') < 0) goto error; Py_ReprLeave((PyObject *)mp); return _PyUnicodeWriter_Finish(&writer); error: Py_ReprLeave((PyObject *)mp); _PyUnicodeWriter_Dealloc(&writer); Py_XDECREF(key); Py_XDECREF(value); return NULL; } static Py_ssize_t dict_length(PyDictObject *mp) { return mp->ma_used; } static PyObject * dict_subscript(PyDictObject *mp, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || value == NULL) { if (!PyDict_CheckExact(mp)) { /* Look up __missing__ method if we're a subclass. */ PyObject *missing, *res; _Py_IDENTIFIER(__missing__); missing = _PyObject_LookupSpecial((PyObject *)mp, &PyId___missing__); if (missing != NULL) { res = PyObject_CallOneArg(missing, key); Py_DECREF(missing); return res; } else if (PyErr_Occurred()) return NULL; } _PyErr_SetKeyError(key); return NULL; } Py_INCREF(value); return value; } static int dict_ass_sub(PyDictObject *mp, PyObject *v, PyObject *w) { if (w == NULL) return PyDict_DelItem((PyObject *)mp, v); else return PyDict_SetItem((PyObject *)mp, v, w); } static PyMappingMethods dict_as_mapping = { (lenfunc)dict_length, /*mp_length*/ (binaryfunc)dict_subscript, /*mp_subscript*/ (objobjargproc)dict_ass_sub, /*mp_ass_subscript*/ }; static PyObject * dict_keys(PyDictObject *mp) { PyObject *v; Py_ssize_t i, j; PyDictKeyEntry *ep; Py_ssize_t n, offset; PyObject **value_ptr; again: n = mp->ma_used; v = PyList_New(n); if (v == NULL) return NULL; if (n != mp->ma_used) { /* Durnit. The allocations caused the dict to resize. * Just start over, this shouldn't normally happen. */ Py_DECREF(v); goto again; } ep = DK_ENTRIES(mp->ma_keys); if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *); } else { value_ptr = &ep[0].me_value; offset = sizeof(PyDictKeyEntry); } for (i = 0, j = 0; j < n; i++) { if (*value_ptr != NULL) { PyObject *key = ep[i].me_key; Py_INCREF(key); PyList_SET_ITEM(v, j, key); j++; } value_ptr = (PyObject **)(((char *)value_ptr) + offset); } assert(j == n); return v; } static PyObject * dict_values(PyDictObject *mp) { PyObject *v; Py_ssize_t i, j; PyDictKeyEntry *ep; Py_ssize_t n, offset; PyObject **value_ptr; again: n = mp->ma_used; v = PyList_New(n); if (v == NULL) return NULL; if (n != mp->ma_used) { /* Durnit. The allocations caused the dict to resize. * Just start over, this shouldn't normally happen. */ Py_DECREF(v); goto again; } ep = DK_ENTRIES(mp->ma_keys); if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *); } else { value_ptr = &ep[0].me_value; offset = sizeof(PyDictKeyEntry); } for (i = 0, j = 0; j < n; i++) { PyObject *value = *value_ptr; value_ptr = (PyObject **)(((char *)value_ptr) + offset); if (value != NULL) { Py_INCREF(value); PyList_SET_ITEM(v, j, value); j++; } } assert(j == n); return v; } static PyObject * dict_items(PyDictObject *mp) { PyObject *v; Py_ssize_t i, j, n; Py_ssize_t offset; PyObject *item, *key; PyDictKeyEntry *ep; PyObject **value_ptr; /* Preallocate the list of tuples, to avoid allocations during * the loop over the items, which could trigger GC, which * could resize the dict. :-( */ again: n = mp->ma_used; v = PyList_New(n); if (v == NULL) return NULL; for (i = 0; i < n; i++) { item = PyTuple_New(2); if (item == NULL) { Py_DECREF(v); return NULL; } PyList_SET_ITEM(v, i, item); } if (n != mp->ma_used) { /* Durnit. The allocations caused the dict to resize. * Just start over, this shouldn't normally happen. */ Py_DECREF(v); goto again; } /* Nothing we do below makes any function calls. */ ep = DK_ENTRIES(mp->ma_keys); if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *); } else { value_ptr = &ep[0].me_value; offset = sizeof(PyDictKeyEntry); } for (i = 0, j = 0; j < n; i++) { PyObject *value = *value_ptr; value_ptr = (PyObject **)(((char *)value_ptr) + offset); if (value != NULL) { key = ep[i].me_key; item = PyList_GET_ITEM(v, j); Py_INCREF(key); PyTuple_SET_ITEM(item, 0, key); Py_INCREF(value); PyTuple_SET_ITEM(item, 1, value); j++; } } assert(j == n); return v; } /*[clinic input] @classmethod dict.fromkeys iterable: object value: object=None / Create a new dictionary with keys from iterable and values set to value. [clinic start generated code]*/ static PyObject * dict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value) /*[clinic end generated code: output=8fb98e4b10384999 input=382ba4855d0f74c3]*/ { return _PyDict_FromKeys((PyObject *)type, iterable, value); } /* Single-arg dict update; used by dict_update_common and operators. */ static int dict_update_arg(PyObject *self, PyObject *arg) { if (PyDict_CheckExact(arg)) { return PyDict_Merge(self, arg, 1); } _Py_IDENTIFIER(keys); PyObject *func; if (_PyObject_LookupAttrId(arg, &PyId_keys, &func) < 0) { return -1; } if (func != NULL) { Py_DECREF(func); return PyDict_Merge(self, arg, 1); } return PyDict_MergeFromSeq2(self, arg, 1); } static int dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, const char *methname) { PyObject *arg = NULL; int result = 0; if (!PyArg_UnpackTuple(args, methname, 0, 1, &arg)) { result = -1; } else if (arg != NULL) { result = dict_update_arg(self, arg); } if (result == 0 && kwds != NULL) { if (PyArg_ValidateKeywordArguments(kwds)) result = PyDict_Merge(self, kwds, 1); else result = -1; } return result; } /* Note: dict.update() uses the METH_VARARGS|METH_KEYWORDS calling convention. Using METH_FASTCALL|METH_KEYWORDS would make dict.update(**dict2) calls slower, see the issue #29312. */ static PyObject * dict_update(PyObject *self, PyObject *args, PyObject *kwds) { if (dict_update_common(self, args, kwds, "update") != -1) Py_RETURN_NONE; return NULL; } /* Update unconditionally replaces existing items. Merge has a 3rd argument 'override'; if set, it acts like Update, otherwise it leaves existing items unchanged. PyDict_{Update,Merge} update/merge from a mapping object. PyDict_MergeFromSeq2 updates/merges from any iterable object producing iterable objects of length 2. */ int PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override) { PyObject *it; /* iter(seq2) */ Py_ssize_t i; /* index into seq2 of current element */ PyObject *item; /* seq2[i] */ PyObject *fast; /* item as a 2-tuple or 2-list */ assert(d != NULL); assert(PyDict_Check(d)); assert(seq2 != NULL); it = PyObject_GetIter(seq2); if (it == NULL) return -1; for (i = 0; ; ++i) { PyObject *key, *value; Py_ssize_t n; fast = NULL; item = PyIter_Next(it); if (item == NULL) { if (PyErr_Occurred()) goto Fail; break; } /* Convert item to sequence, and verify length 2. */ fast = PySequence_Fast(item, ""); if (fast == NULL) { if (PyErr_ExceptionMatches(PyExc_TypeError)) PyErr_Format(PyExc_TypeError, "cannot convert dictionary update " "sequence element #%zd to a sequence", i); goto Fail; } n = PySequence_Fast_GET_SIZE(fast); if (n != 2) { PyErr_Format(PyExc_ValueError, "dictionary update sequence element #%zd " "has length %zd; 2 is required", i, n); goto Fail; } /* Update/merge with this (key, value) pair. */ key = PySequence_Fast_GET_ITEM(fast, 0); value = PySequence_Fast_GET_ITEM(fast, 1); Py_INCREF(key); Py_INCREF(value); if (override) { if (PyDict_SetItem(d, key, value) < 0) { Py_DECREF(key); Py_DECREF(value); goto Fail; } } else { if (PyDict_SetDefault(d, key, value) == NULL) { Py_DECREF(key); Py_DECREF(value); goto Fail; } } Py_DECREF(key); Py_DECREF(value); Py_DECREF(fast); Py_DECREF(item); } i = 0; ASSERT_CONSISTENT(d); goto Return; Fail: Py_XDECREF(item); Py_XDECREF(fast); i = -1; Return: Py_DECREF(it); return Py_SAFE_DOWNCAST(i, Py_ssize_t, int); } static int dict_merge(PyObject *a, PyObject *b, int override) { PyDictObject *mp, *other; Py_ssize_t i, n; PyDictKeyEntry *entry, *ep0; assert(0 <= override && override <= 2); /* We accept for the argument either a concrete dictionary object, * or an abstract "mapping" object. For the former, we can do * things quite efficiently. For the latter, we only require that * PyMapping_Keys() and PyObject_GetItem() be supported. */ if (a == NULL || !PyDict_Check(a) || b == NULL) { PyErr_BadInternalCall(); return -1; } mp = (PyDictObject*)a; if (PyDict_Check(b) && (Py_TYPE(b)->tp_iter == (getiterfunc)dict_iter)) { other = (PyDictObject*)b; if (other == mp || other->ma_used == 0) /* a.update(a) or a.update({}); nothing to do */ return 0; if (mp->ma_used == 0) { /* Since the target dict is empty, PyDict_GetItem() * always returns NULL. Setting override to 1 * skips the unnecessary test. */ override = 1; PyDictKeysObject *okeys = other->ma_keys; // If other is clean, combined, and just allocated, just clone it. if (other->ma_values == NULL && other->ma_used == okeys->dk_nentries && (okeys->dk_size == PyDict_MINSIZE || USABLE_FRACTION(okeys->dk_size/2) < other->ma_used)) { PyDictKeysObject *keys = clone_combined_dict_keys(other); if (keys == NULL) { return -1; } dictkeys_decref(mp->ma_keys); mp->ma_keys = keys; if (mp->ma_values != NULL) { if (mp->ma_values != empty_values) { free_values(mp->ma_values); } mp->ma_values = NULL; } mp->ma_used = other->ma_used; mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(mp); if (_PyObject_GC_IS_TRACKED(other) && !_PyObject_GC_IS_TRACKED(mp)) { /* Maintain tracking. */ _PyObject_GC_TRACK(mp); } return 0; } } /* Do one big resize at the start, rather than * incrementally resizing as we insert new items. Expect * that there will be no (or few) overlapping keys. */ if (USABLE_FRACTION(mp->ma_keys->dk_size) < other->ma_used) { if (dictresize(mp, estimate_keysize(mp->ma_used + other->ma_used))) { return -1; } } ep0 = DK_ENTRIES(other->ma_keys); for (i = 0, n = other->ma_keys->dk_nentries; i < n; i++) { PyObject *key, *value; Py_hash_t hash; entry = &ep0[i]; key = entry->me_key; hash = entry->me_hash; if (other->ma_values) value = other->ma_values[i]; else value = entry->me_value; if (value != NULL) { int err = 0; Py_INCREF(key); Py_INCREF(value); if (override == 1) err = insertdict(mp, key, hash, value); else { err = _PyDict_Contains_KnownHash(a, key, hash); if (err == 0) { err = insertdict(mp, key, hash, value); } else if (err > 0) { if (override != 0) { _PyErr_SetKeyError(key); Py_DECREF(value); Py_DECREF(key); return -1; } err = 0; } } Py_DECREF(value); Py_DECREF(key); if (err != 0) return -1; if (n != other->ma_keys->dk_nentries) { PyErr_SetString(PyExc_RuntimeError, "dict mutated during update"); return -1; } } } } else { /* Do it the generic, slower way */ PyObject *keys = PyMapping_Keys(b); PyObject *iter; PyObject *key, *value; int status; if (keys == NULL) /* Docstring says this is equivalent to E.keys() so * if E doesn't have a .keys() method we want * AttributeError to percolate up. Might as well * do the same for any other error. */ return -1; iter = PyObject_GetIter(keys); Py_DECREF(keys); if (iter == NULL) return -1; for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) { if (override != 1) { status = PyDict_Contains(a, key); if (status != 0) { if (status > 0) { if (override == 0) { Py_DECREF(key); continue; } _PyErr_SetKeyError(key); } Py_DECREF(key); Py_DECREF(iter); return -1; } } value = PyObject_GetItem(b, key); if (value == NULL) { Py_DECREF(iter); Py_DECREF(key); return -1; } status = PyDict_SetItem(a, key, value); Py_DECREF(key); Py_DECREF(value); if (status < 0) { Py_DECREF(iter); return -1; } } Py_DECREF(iter); if (PyErr_Occurred()) /* Iterator completed, via error */ return -1; } ASSERT_CONSISTENT(a); return 0; } int PyDict_Update(PyObject *a, PyObject *b) { return dict_merge(a, b, 1); } int PyDict_Merge(PyObject *a, PyObject *b, int override) { /* XXX Deprecate override not in (0, 1). */ return dict_merge(a, b, override != 0); } int _PyDict_MergeEx(PyObject *a, PyObject *b, int override) { return dict_merge(a, b, override); } static PyObject * dict_copy(PyDictObject *mp, PyObject *Py_UNUSED(ignored)) { return PyDict_Copy((PyObject*)mp); } PyObject * PyDict_Copy(PyObject *o) { PyObject *copy; PyDictObject *mp; Py_ssize_t i, n; if (o == NULL || !PyDict_Check(o)) { PyErr_BadInternalCall(); return NULL; } mp = (PyDictObject *)o; if (mp->ma_used == 0) { /* The dict is empty; just return a new dict. */ return PyDict_New(); } if (_PyDict_HasSplitTable(mp)) { PyDictObject *split_copy; Py_ssize_t size = USABLE_FRACTION(DK_SIZE(mp->ma_keys)); PyObject **newvalues; newvalues = new_values(size); if (newvalues == NULL) return PyErr_NoMemory(); split_copy = PyObject_GC_New(PyDictObject, &PyDict_Type); if (split_copy == NULL) { free_values(newvalues); return NULL; } split_copy->ma_values = newvalues; split_copy->ma_keys = mp->ma_keys; split_copy->ma_used = mp->ma_used; split_copy->ma_version_tag = DICT_NEXT_VERSION(); dictkeys_incref(mp->ma_keys); for (i = 0, n = size; i < n; i++) { PyObject *value = mp->ma_values[i]; Py_XINCREF(value); split_copy->ma_values[i] = value; } if (_PyObject_GC_IS_TRACKED(mp)) _PyObject_GC_TRACK(split_copy); return (PyObject *)split_copy; } if (Py_TYPE(mp)->tp_iter == (getiterfunc)dict_iter && mp->ma_values == NULL && (mp->ma_used >= (mp->ma_keys->dk_nentries * 2) / 3)) { /* Use fast-copy if: (1) type(mp) doesn't override tp_iter; and (2) 'mp' is not a split-dict; and (3) if 'mp' is non-compact ('del' operation does not resize dicts), do fast-copy only if it has at most 1/3 non-used keys. The last condition (3) is important to guard against a pathological case when a large dict is almost emptied with multiple del/pop operations and copied after that. In cases like this, we defer to PyDict_Merge, which produces a compacted copy. */ PyDictKeysObject *keys = clone_combined_dict_keys(mp); if (keys == NULL) { return NULL; } PyDictObject *new = (PyDictObject *)new_dict(keys, NULL); if (new == NULL) { /* In case of an error, `new_dict()` takes care of cleaning up `keys`. */ return NULL; } new->ma_used = mp->ma_used; ASSERT_CONSISTENT(new); if (_PyObject_GC_IS_TRACKED(mp)) { /* Maintain tracking. */ _PyObject_GC_TRACK(new); } return (PyObject *)new; } copy = PyDict_New(); if (copy == NULL) return NULL; if (dict_merge(copy, o, 1) == 0) return copy; Py_DECREF(copy); return NULL; } Py_ssize_t PyDict_Size(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return -1; } return ((PyDictObject *)mp)->ma_used; } PyObject * PyDict_Keys(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return NULL; } return dict_keys((PyDictObject *)mp); } PyObject * PyDict_Values(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return NULL; } return dict_values((PyDictObject *)mp); } PyObject * PyDict_Items(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return NULL; } return dict_items((PyDictObject *)mp); } /* Return 1 if dicts equal, 0 if not, -1 if error. * Gets out as soon as any difference is detected. * Uses only Py_EQ comparison. */ static int dict_equal(PyDictObject *a, PyDictObject *b) { Py_ssize_t i; if (a->ma_used != b->ma_used) /* can't be equal if # of entries differ */ return 0; /* Same # of entries -- check all of 'em. Exit early on any diff. */ for (i = 0; i < a->ma_keys->dk_nentries; i++) { PyDictKeyEntry *ep = &DK_ENTRIES(a->ma_keys)[i]; PyObject *aval; if (a->ma_values) aval = a->ma_values[i]; else aval = ep->me_value; if (aval != NULL) { int cmp; PyObject *bval; PyObject *key = ep->me_key; /* temporarily bump aval's refcount to ensure it stays alive until we're done with it */ Py_INCREF(aval); /* ditto for key */ Py_INCREF(key); /* reuse the known hash value */ b->ma_keys->dk_lookup(b, key, ep->me_hash, &bval); if (bval == NULL) { Py_DECREF(key); Py_DECREF(aval); if (PyErr_Occurred()) return -1; return 0; } Py_INCREF(bval); cmp = PyObject_RichCompareBool(aval, bval, Py_EQ); Py_DECREF(key); Py_DECREF(aval); Py_DECREF(bval); if (cmp <= 0) /* error or not equal */ return cmp; } } return 1; } static PyObject * dict_richcompare(PyObject *v, PyObject *w, int op) { int cmp; PyObject *res; if (!PyDict_Check(v) || !PyDict_Check(w)) { res = Py_NotImplemented; } else if (op == Py_EQ || op == Py_NE) { cmp = dict_equal((PyDictObject *)v, (PyDictObject *)w); if (cmp < 0) return NULL; res = (cmp == (op == Py_EQ)) ? Py_True : Py_False; } else res = Py_NotImplemented; Py_INCREF(res); return res; } /*[clinic input] @coexist dict.__contains__ key: object / True if the dictionary has the specified key, else False. [clinic start generated code]*/ static PyObject * dict___contains__(PyDictObject *self, PyObject *key) /*[clinic end generated code: output=a3d03db709ed6e6b input=fe1cb42ad831e820]*/ { register PyDictObject *mp = self; Py_hash_t hash; Py_ssize_t ix; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || value == NULL) Py_RETURN_FALSE; Py_RETURN_TRUE; } /*[clinic input] dict.get key: object default: object = None / Return the value for key if key is in the dictionary, else default. [clinic start generated code]*/ static PyObject * dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value) /*[clinic end generated code: output=bba707729dee05bf input=279ddb5790b6b107]*/ { PyObject *val = NULL; Py_hash_t hash; Py_ssize_t ix; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (self->ma_keys->dk_lookup) (self, key, hash, &val); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || val == NULL) { val = default_value; } Py_INCREF(val); return val; } PyObject * PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj) { PyDictObject *mp = (PyDictObject *)d; PyObject *value; Py_hash_t hash; if (!PyDict_Check(d)) { PyErr_BadInternalCall(); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } if (mp->ma_keys == Py_EMPTY_KEYS) { if (insert_to_emptydict(mp, key, hash, defaultobj) < 0) { return NULL; } return defaultobj; } if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) { if (insertion_resize(mp) < 0) return NULL; } Py_ssize_t ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (_PyDict_HasSplitTable(mp) && ((ix >= 0 && value == NULL && mp->ma_used != ix) || (ix == DKIX_EMPTY && mp->ma_used != mp->ma_keys->dk_nentries))) { if (insertion_resize(mp) < 0) { return NULL; } ix = DKIX_EMPTY; } if (ix == DKIX_EMPTY) { PyDictKeyEntry *ep, *ep0; value = defaultobj; if (mp->ma_keys->dk_usable <= 0) { if (insertion_resize(mp) < 0) { return NULL; } } if (!PyUnicode_CheckExact(key) && mp->ma_keys->dk_lookup != lookdict) { mp->ma_keys->dk_lookup = lookdict; } Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash); ep0 = DK_ENTRIES(mp->ma_keys); ep = &ep0[mp->ma_keys->dk_nentries]; dictkeys_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries); Py_INCREF(key); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); ep->me_key = key; ep->me_hash = hash; if (_PyDict_HasSplitTable(mp)) { assert(mp->ma_values[mp->ma_keys->dk_nentries] == NULL); mp->ma_values[mp->ma_keys->dk_nentries] = value; } else { ep->me_value = value; } mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); mp->ma_keys->dk_usable--; mp->ma_keys->dk_nentries++; assert(mp->ma_keys->dk_usable >= 0); } else if (value == NULL) { value = defaultobj; assert(_PyDict_HasSplitTable(mp)); assert(ix == mp->ma_used); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); mp->ma_values[ix] = value; mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); } ASSERT_CONSISTENT(mp); return value; } /*[clinic input] dict.setdefault key: object default: object = None / Insert key with a value of default if key is not in the dictionary. Return the value for key if key is in the dictionary, else default. [clinic start generated code]*/ static PyObject * dict_setdefault_impl(PyDictObject *self, PyObject *key, PyObject *default_value) /*[clinic end generated code: output=f8c1101ebf69e220 input=0f063756e815fd9d]*/ { PyObject *val; val = PyDict_SetDefault((PyObject *)self, key, default_value); Py_XINCREF(val); return val; } static PyObject * dict_clear(PyDictObject *mp, PyObject *Py_UNUSED(ignored)) { PyDict_Clear((PyObject *)mp); Py_RETURN_NONE; } /*[clinic input] dict.pop key: object default: object = NULL / D.pop(k[,d]) -> v, remove specified key and return the corresponding value. If the key is not found, return the default if given; otherwise, raise a KeyError. [clinic start generated code]*/ static PyObject * dict_pop_impl(PyDictObject *self, PyObject *key, PyObject *default_value) /*[clinic end generated code: output=3abb47b89f24c21c input=e221baa01044c44c]*/ { return _PyDict_Pop((PyObject*)self, key, default_value); } /*[clinic input] dict.popitem Remove and return a (key, value) pair as a 2-tuple. Pairs are returned in LIFO (last-in, first-out) order. Raises KeyError if the dict is empty. [clinic start generated code]*/ static PyObject * dict_popitem_impl(PyDictObject *self) /*[clinic end generated code: output=e65fcb04420d230d input=1c38a49f21f64941]*/ { Py_ssize_t i, j; PyDictKeyEntry *ep0, *ep; PyObject *res; /* Allocate the result tuple before checking the size. Believe it * or not, this allocation could trigger a garbage collection which * could empty the dict, so if we checked the size first and that * happened, the result would be an infinite loop (searching for an * entry that no longer exists). Note that the usual popitem() * idiom is "while d: k, v = d.popitem()". so needing to throw the * tuple away if the dict *is* empty isn't a significant * inefficiency -- possible, but unlikely in practice. */ res = PyTuple_New(2); if (res == NULL) return NULL; if (self->ma_used == 0) { Py_DECREF(res); PyErr_SetString(PyExc_KeyError, "popitem(): dictionary is empty"); return NULL; } /* Convert split table to combined table */ if (self->ma_keys->dk_lookup == lookdict_split) { if (dictresize(self, DK_SIZE(self->ma_keys))) { Py_DECREF(res); return NULL; } } ENSURE_ALLOWS_DELETIONS(self); /* Pop last item */ ep0 = DK_ENTRIES(self->ma_keys); i = self->ma_keys->dk_nentries - 1; while (i >= 0 && ep0[i].me_value == NULL) { i--; } assert(i >= 0); ep = &ep0[i]; j = lookdict_index(self->ma_keys, ep->me_hash, i); assert(j >= 0); assert(dictkeys_get_index(self->ma_keys, j) == i); dictkeys_set_index(self->ma_keys, j, DKIX_DUMMY); PyTuple_SET_ITEM(res, 0, ep->me_key); PyTuple_SET_ITEM(res, 1, ep->me_value); ep->me_key = NULL; ep->me_value = NULL; /* We can't dk_usable++ since there is DKIX_DUMMY in indices */ self->ma_keys->dk_nentries = i; self->ma_used--; self->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(self); return res; } static int dict_traverse(PyObject *op, visitproc visit, void *arg) { PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n = keys->dk_nentries; if (keys->dk_lookup == lookdict) { for (i = 0; i < n; i++) { if (entries[i].me_value != NULL) { Py_VISIT(entries[i].me_value); Py_VISIT(entries[i].me_key); } } } else { if (mp->ma_values != NULL) { for (i = 0; i < n; i++) { Py_VISIT(mp->ma_values[i]); } } else { for (i = 0; i < n; i++) { Py_VISIT(entries[i].me_value); } } } return 0; } static int dict_tp_clear(PyObject *op) { PyDict_Clear(op); return 0; } static PyObject *dictiter_new(PyDictObject *, PyTypeObject *); Py_ssize_t _PyDict_SizeOf(PyDictObject *mp) { Py_ssize_t size, usable, res; size = DK_SIZE(mp->ma_keys); usable = USABLE_FRACTION(size); res = _PyObject_SIZE(Py_TYPE(mp)); if (mp->ma_values) res += usable * sizeof(PyObject*); /* If the dictionary is split, the keys portion is accounted-for in the type object. */ if (mp->ma_keys->dk_refcnt == 1) res += (sizeof(PyDictKeysObject) + DK_IXSIZE(mp->ma_keys) * size + sizeof(PyDictKeyEntry) * usable); return res; } Py_ssize_t _PyDict_KeysSize(PyDictKeysObject *keys) { return (sizeof(PyDictKeysObject) + DK_IXSIZE(keys) * DK_SIZE(keys) + USABLE_FRACTION(DK_SIZE(keys)) * sizeof(PyDictKeyEntry)); } static PyObject * dict_sizeof(PyDictObject *mp, PyObject *Py_UNUSED(ignored)) { return PyLong_FromSsize_t(_PyDict_SizeOf(mp)); } static PyObject * dict_or(PyObject *self, PyObject *other) { if (!PyDict_Check(self) || !PyDict_Check(other)) { Py_RETURN_NOTIMPLEMENTED; } PyObject *new = PyDict_Copy(self); if (new == NULL) { return NULL; } if (dict_update_arg(new, other)) { Py_DECREF(new); return NULL; } return new; } static PyObject * dict_ior(PyObject *self, PyObject *other) { if (dict_update_arg(self, other)) { return NULL; } Py_INCREF(self); return self; } PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]"); PyDoc_STRVAR(sizeof__doc__, "D.__sizeof__() -> size of D in memory, in bytes"); PyDoc_STRVAR(update__doc__, "D.update([E, ]**F) -> None. Update D from dict/iterable E and F.\n\ If E is present and has a .keys() method, then does: for k in E: D[k] = E[k]\n\ If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v\n\ In either case, this is followed by: for k in F: D[k] = F[k]"); PyDoc_STRVAR(clear__doc__, "D.clear() -> None. Remove all items from D."); PyDoc_STRVAR(copy__doc__, "D.copy() -> a shallow copy of D"); /* Forward */ static PyObject *dictkeys_new(PyObject *, PyObject *); static PyObject *dictitems_new(PyObject *, PyObject *); static PyObject *dictvalues_new(PyObject *, PyObject *); PyDoc_STRVAR(keys__doc__, "D.keys() -> a set-like object providing a view on D's keys"); PyDoc_STRVAR(items__doc__, "D.items() -> a set-like object providing a view on D's items"); PyDoc_STRVAR(values__doc__, "D.values() -> an object providing a view on D's values"); static PyMethodDef mapp_methods[] = { DICT___CONTAINS___METHODDEF {"__getitem__", (PyCFunction)(void(*)(void))dict_subscript, METH_O | METH_COEXIST, getitem__doc__}, {"__sizeof__", (PyCFunction)(void(*)(void))dict_sizeof, METH_NOARGS, sizeof__doc__}, DICT_GET_METHODDEF DICT_SETDEFAULT_METHODDEF DICT_POP_METHODDEF DICT_POPITEM_METHODDEF {"keys", dictkeys_new, METH_NOARGS, keys__doc__}, {"items", dictitems_new, METH_NOARGS, items__doc__}, {"values", dictvalues_new, METH_NOARGS, values__doc__}, {"update", (PyCFunction)(void(*)(void))dict_update, METH_VARARGS | METH_KEYWORDS, update__doc__}, DICT_FROMKEYS_METHODDEF {"clear", (PyCFunction)dict_clear, METH_NOARGS, clear__doc__}, {"copy", (PyCFunction)dict_copy, METH_NOARGS, copy__doc__}, DICT___REVERSED___METHODDEF {"__class_getitem__", (PyCFunction)Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, {NULL, NULL} /* sentinel */ }; /* Return 1 if `key` is in dict `op`, 0 if not, and -1 on error. */ int PyDict_Contains(PyObject *op, PyObject *key) { Py_hash_t hash; Py_ssize_t ix; PyDictObject *mp = (PyDictObject *)op; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return -1; return (ix != DKIX_EMPTY && value != NULL); } /* Internal version of PyDict_Contains used when the hash value is already known */ int _PyDict_Contains_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) { PyDictObject *mp = (PyDictObject *)op; PyObject *value; Py_ssize_t ix; ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return -1; return (ix != DKIX_EMPTY && value != NULL); } int _PyDict_ContainsId(PyObject *op, struct _Py_Identifier *key) { PyObject *kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) { return -1; } return PyDict_Contains(op, kv); } /* Hack to implement "key in dict" */ static PySequenceMethods dict_as_sequence = { 0, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ PyDict_Contains, /* sq_contains */ 0, /* sq_inplace_concat */ 0, /* sq_inplace_repeat */ }; static PyNumberMethods dict_as_number = { .nb_or = dict_or, .nb_inplace_or = dict_ior, }; static PyObject * dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *self; PyDictObject *d; assert(type != NULL && type->tp_alloc != NULL); self = type->tp_alloc(type, 0); if (self == NULL) return NULL; d = (PyDictObject *)self; /* The object has been implicitly tracked by tp_alloc */ if (type == &PyDict_Type) { _PyObject_GC_UNTRACK(d); } d->ma_used = 0; d->ma_version_tag = DICT_NEXT_VERSION(); dictkeys_incref(Py_EMPTY_KEYS); d->ma_keys = Py_EMPTY_KEYS; d->ma_values = empty_values; ASSERT_CONSISTENT(d); return self; } static int dict_init(PyObject *self, PyObject *args, PyObject *kwds) { return dict_update_common(self, args, kwds, "dict"); } static PyObject * dict_vectorcall(PyObject *type, PyObject * const*args, size_t nargsf, PyObject *kwnames) { assert(PyType_Check(type)); Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); if (!_PyArg_CheckPositional("dict", nargs, 0, 1)) { return NULL; } PyObject *self = dict_new((PyTypeObject *)type, NULL, NULL); if (self == NULL) { return NULL; } if (nargs == 1) { if (dict_update_arg(self, args[0]) < 0) { Py_DECREF(self); return NULL; } args++; } if (kwnames != NULL) { for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(kwnames); i++) { if (PyDict_SetItem(self, PyTuple_GET_ITEM(kwnames, i), args[i]) < 0) { Py_DECREF(self); return NULL; } } } return self; } static PyObject * dict_iter(PyDictObject *dict) { return dictiter_new(dict, &PyDictIterKey_Type); } PyDoc_STRVAR(dictionary_doc, "dict() -> new empty dictionary\n" "dict(mapping) -> new dictionary initialized from a mapping object's\n" " (key, value) pairs\n" "dict(iterable) -> new dictionary initialized as if via:\n" " d = {}\n" " for k, v in iterable:\n" " d[k] = v\n" "dict(**kwargs) -> new dictionary initialized with the name=value pairs\n" " in the keyword argument list. For example: dict(one=1, two=2)"); PyTypeObject PyDict_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict", sizeof(PyDictObject), 0, (destructor)dict_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dict_repr, /* tp_repr */ &dict_as_number, /* tp_as_number */ &dict_as_sequence, /* tp_as_sequence */ &dict_as_mapping, /* tp_as_mapping */ PyObject_HashNotImplemented, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_DICT_SUBCLASS | _Py_TPFLAGS_MATCH_SELF | Py_TPFLAGS_MAPPING, /* tp_flags */ dictionary_doc, /* tp_doc */ dict_traverse, /* tp_traverse */ dict_tp_clear, /* tp_clear */ dict_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dict_iter, /* tp_iter */ 0, /* tp_iternext */ mapp_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ dict_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ dict_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ .tp_vectorcall = dict_vectorcall, }; /* For backward compatibility with old dictionary interface */ PyObject * PyDict_GetItemString(PyObject *v, const char *key) { PyObject *kv, *rv; kv = PyUnicode_FromString(key); if (kv == NULL) { PyErr_Clear(); return NULL; } rv = PyDict_GetItem(v, kv); Py_DECREF(kv); return rv; } int _PyDict_SetItemId(PyObject *v, struct _Py_Identifier *key, PyObject *item) { PyObject *kv; kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) return -1; return PyDict_SetItem(v, kv, item); } int PyDict_SetItemString(PyObject *v, const char *key, PyObject *item) { PyObject *kv; int err; kv = PyUnicode_FromString(key); if (kv == NULL) return -1; PyUnicode_InternInPlace(&kv); /* XXX Should we really? */ err = PyDict_SetItem(v, kv, item); Py_DECREF(kv); return err; } int _PyDict_DelItemId(PyObject *v, _Py_Identifier *key) { PyObject *kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) return -1; return PyDict_DelItem(v, kv); } int PyDict_DelItemString(PyObject *v, const char *key) { PyObject *kv; int err; kv = PyUnicode_FromString(key); if (kv == NULL) return -1; err = PyDict_DelItem(v, kv); Py_DECREF(kv); return err; } /* Dictionary iterator types */ typedef struct { PyObject_HEAD PyDictObject *di_dict; /* Set to NULL when iterator is exhausted */ Py_ssize_t di_used; Py_ssize_t di_pos; PyObject* di_result; /* reusable result tuple for iteritems */ Py_ssize_t len; } dictiterobject; static PyObject * dictiter_new(PyDictObject *dict, PyTypeObject *itertype) { dictiterobject *di; di = PyObject_GC_New(dictiterobject, itertype); if (di == NULL) { return NULL; } Py_INCREF(dict); di->di_dict = dict; di->di_used = dict->ma_used; di->len = dict->ma_used; if (itertype == &PyDictRevIterKey_Type || itertype == &PyDictRevIterItem_Type || itertype == &PyDictRevIterValue_Type) { if (dict->ma_values) { di->di_pos = dict->ma_used - 1; } else { di->di_pos = dict->ma_keys->dk_nentries - 1; } } else { di->di_pos = 0; } if (itertype == &PyDictIterItem_Type || itertype == &PyDictRevIterItem_Type) { di->di_result = PyTuple_Pack(2, Py_None, Py_None); if (di->di_result == NULL) { Py_DECREF(di); return NULL; } } else { di->di_result = NULL; } _PyObject_GC_TRACK(di); return (PyObject *)di; } static void dictiter_dealloc(dictiterobject *di) { /* bpo-31095: UnTrack is needed before calling any callbacks */ _PyObject_GC_UNTRACK(di); Py_XDECREF(di->di_dict); Py_XDECREF(di->di_result); PyObject_GC_Del(di); } static int dictiter_traverse(dictiterobject *di, visitproc visit, void *arg) { Py_VISIT(di->di_dict); Py_VISIT(di->di_result); return 0; } static PyObject * dictiter_len(dictiterobject *di, PyObject *Py_UNUSED(ignored)) { Py_ssize_t len = 0; if (di->di_dict != NULL && di->di_used == di->di_dict->ma_used) len = di->len; return PyLong_FromSize_t(len); } PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); static PyObject * dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyMethodDef dictiter_methods[] = { {"__length_hint__", (PyCFunction)(void(*)(void))dictiter_len, METH_NOARGS, length_hint_doc}, {"__reduce__", (PyCFunction)(void(*)(void))dictiter_reduce, METH_NOARGS, reduce_doc}, {NULL, NULL} /* sentinel */ }; static PyObject* dictiter_iternextkey(dictiterobject *di) { PyObject *key; Py_ssize_t i; PyDictKeysObject *k; PyDictObject *d = di->di_dict; if (d == NULL) return NULL; assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } i = di->di_pos; k = d->ma_keys; assert(i >= 0); if (d->ma_values) { if (i >= d->ma_used) goto fail; key = DK_ENTRIES(k)[i].me_key; assert(d->ma_values[i] != NULL); } else { Py_ssize_t n = k->dk_nentries; PyDictKeyEntry *entry_ptr = &DK_ENTRIES(k)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) goto fail; key = entry_ptr->me_key; } // We found an element (key), but did not expect it if (di->len == 0) { PyErr_SetString(PyExc_RuntimeError, "dictionary keys changed during iteration"); goto fail; } di->di_pos = i+1; di->len--; Py_INCREF(key); return key; fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictIterKey_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_keyiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)dictiter_iternextkey, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject * dictiter_iternextvalue(dictiterobject *di) { PyObject *value; Py_ssize_t i; PyDictObject *d = di->di_dict; if (d == NULL) return NULL; assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } i = di->di_pos; assert(i >= 0); if (d->ma_values) { if (i >= d->ma_used) goto fail; value = d->ma_values[i]; assert(value != NULL); } else { Py_ssize_t n = d->ma_keys->dk_nentries; PyDictKeyEntry *entry_ptr = &DK_ENTRIES(d->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) goto fail; value = entry_ptr->me_value; } // We found an element, but did not expect it if (di->len == 0) { PyErr_SetString(PyExc_RuntimeError, "dictionary keys changed during iteration"); goto fail; } di->di_pos = i+1; di->len--; Py_INCREF(value); return value; fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictIterValue_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_valueiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)dictiter_iternextvalue, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject * dictiter_iternextitem(dictiterobject *di) { PyObject *key, *value, *result; Py_ssize_t i; PyDictObject *d = di->di_dict; if (d == NULL) return NULL; assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } i = di->di_pos; assert(i >= 0); if (d->ma_values) { if (i >= d->ma_used) goto fail; key = DK_ENTRIES(d->ma_keys)[i].me_key; value = d->ma_values[i]; assert(value != NULL); } else { Py_ssize_t n = d->ma_keys->dk_nentries; PyDictKeyEntry *entry_ptr = &DK_ENTRIES(d->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) goto fail; key = entry_ptr->me_key; value = entry_ptr->me_value; } // We found an element, but did not expect it if (di->len == 0) { PyErr_SetString(PyExc_RuntimeError, "dictionary keys changed during iteration"); goto fail; } di->di_pos = i+1; di->len--; Py_INCREF(key); Py_INCREF(value); result = di->di_result; if (Py_REFCNT(result) == 1) { PyObject *oldkey = PyTuple_GET_ITEM(result, 0); PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); // bpo-42536: The GC may have untracked this result tuple. Since we're // recycling it, make sure it's tracked again: if (!_PyObject_GC_IS_TRACKED(result)) { _PyObject_GC_TRACK(result); } } else { result = PyTuple_New(2); if (result == NULL) return NULL; PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ } return result; fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictIterItem_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_itemiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)dictiter_iternextitem, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; /* dictreviter */ static PyObject * dictreviter_iternext(dictiterobject *di) { PyDictObject *d = di->di_dict; if (d == NULL) { return NULL; } assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } Py_ssize_t i = di->di_pos; PyDictKeysObject *k = d->ma_keys; PyObject *key, *value, *result; if (i < 0) { goto fail; } if (d->ma_values) { key = DK_ENTRIES(k)[i].me_key; value = d->ma_values[i]; assert (value != NULL); } else { PyDictKeyEntry *entry_ptr = &DK_ENTRIES(k)[i]; while (entry_ptr->me_value == NULL) { if (--i < 0) { goto fail; } entry_ptr--; } key = entry_ptr->me_key; value = entry_ptr->me_value; } di->di_pos = i-1; di->len--; if (Py_IS_TYPE(di, &PyDictRevIterKey_Type)) { Py_INCREF(key); return key; } else if (Py_IS_TYPE(di, &PyDictRevIterValue_Type)) { Py_INCREF(value); return value; } else if (Py_IS_TYPE(di, &PyDictRevIterItem_Type)) { Py_INCREF(key); Py_INCREF(value); result = di->di_result; if (Py_REFCNT(result) == 1) { PyObject *oldkey = PyTuple_GET_ITEM(result, 0); PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); // bpo-42536: The GC may have untracked this result tuple. Since // we're recycling it, make sure it's tracked again: if (!_PyObject_GC_IS_TRACKED(result)) { _PyObject_GC_TRACK(result); } } else { result = PyTuple_New(2); if (result == NULL) { return NULL; } PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ } return result; } else { Py_UNREACHABLE(); } fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictRevIterKey_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_reversekeyiterator", sizeof(dictiterobject), .tp_dealloc = (destructor)dictiter_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_traverse = (traverseproc)dictiter_traverse, .tp_iter = PyObject_SelfIter, .tp_iternext = (iternextfunc)dictreviter_iternext, .tp_methods = dictiter_methods }; /*[clinic input] dict.__reversed__ Return a reverse iterator over the dict keys. [clinic start generated code]*/ static PyObject * dict___reversed___impl(PyDictObject *self) /*[clinic end generated code: output=e674483336d1ed51 input=23210ef3477d8c4d]*/ { assert (PyDict_Check(self)); return dictiter_new(self, &PyDictRevIterKey_Type); } static PyObject * dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)) { _Py_IDENTIFIER(iter); /* copy the iterator state */ dictiterobject tmp = *di; Py_XINCREF(tmp.di_dict); PyObject *list = PySequence_List((PyObject*)&tmp); Py_XDECREF(tmp.di_dict); if (list == NULL) { return NULL; } return Py_BuildValue("N(N)", _PyEval_GetBuiltinId(&PyId_iter), list); } PyTypeObject PyDictRevIterItem_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_reverseitemiterator", sizeof(dictiterobject), .tp_dealloc = (destructor)dictiter_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_traverse = (traverseproc)dictiter_traverse, .tp_iter = PyObject_SelfIter, .tp_iternext = (iternextfunc)dictreviter_iternext, .tp_methods = dictiter_methods }; PyTypeObject PyDictRevIterValue_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_reversevalueiterator", sizeof(dictiterobject), .tp_dealloc = (destructor)dictiter_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_traverse = (traverseproc)dictiter_traverse, .tp_iter = PyObject_SelfIter, .tp_iternext = (iternextfunc)dictreviter_iternext, .tp_methods = dictiter_methods }; /***********************************************/ /* View objects for keys(), items(), values(). */ /***********************************************/ /* The instance lay-out is the same for all three; but the type differs. */ static void dictview_dealloc(_PyDictViewObject *dv) { /* bpo-31095: UnTrack is needed before calling any callbacks */ _PyObject_GC_UNTRACK(dv); Py_XDECREF(dv->dv_dict); PyObject_GC_Del(dv); } static int dictview_traverse(_PyDictViewObject *dv, visitproc visit, void *arg) { Py_VISIT(dv->dv_dict); return 0; } static Py_ssize_t dictview_len(_PyDictViewObject *dv) { Py_ssize_t len = 0; if (dv->dv_dict != NULL) len = dv->dv_dict->ma_used; return len; } PyObject * _PyDictView_New(PyObject *dict, PyTypeObject *type) { _PyDictViewObject *dv; if (dict == NULL) { PyErr_BadInternalCall(); return NULL; } if (!PyDict_Check(dict)) { /* XXX Get rid of this restriction later */ PyErr_Format(PyExc_TypeError, "%s() requires a dict argument, not '%s'", type->tp_name, Py_TYPE(dict)->tp_name); return NULL; } dv = PyObject_GC_New(_PyDictViewObject, type); if (dv == NULL) return NULL; Py_INCREF(dict); dv->dv_dict = (PyDictObject *)dict; _PyObject_GC_TRACK(dv); return (PyObject *)dv; } static PyObject * dictview_mapping(PyObject *view, void *Py_UNUSED(ignored)) { assert(view != NULL); assert(PyDictKeys_Check(view) || PyDictValues_Check(view) || PyDictItems_Check(view)); PyObject *mapping = (PyObject *)((_PyDictViewObject *)view)->dv_dict; return PyDictProxy_New(mapping); } static PyGetSetDef dictview_getset[] = { {"mapping", dictview_mapping, (setter)NULL, "dictionary that this view refers to", NULL}, {0} }; /* TODO(guido): The views objects are not complete: * support more set operations * support arbitrary mappings? - either these should be static or exported in dictobject.h - if public then they should probably be in builtins */ /* Return 1 if self is a subset of other, iterating over self; 0 if not; -1 if an error occurred. */ static int all_contained_in(PyObject *self, PyObject *other) { PyObject *iter = PyObject_GetIter(self); int ok = 1; if (iter == NULL) return -1; for (;;) { PyObject *next = PyIter_Next(iter); if (next == NULL) { if (PyErr_Occurred()) ok = -1; break; } ok = PySequence_Contains(other, next); Py_DECREF(next); if (ok <= 0) break; } Py_DECREF(iter); return ok; } static PyObject * dictview_richcompare(PyObject *self, PyObject *other, int op) { Py_ssize_t len_self, len_other; int ok; PyObject *result; assert(self != NULL); assert(PyDictViewSet_Check(self)); assert(other != NULL); if (!PyAnySet_Check(other) && !PyDictViewSet_Check(other)) Py_RETURN_NOTIMPLEMENTED; len_self = PyObject_Size(self); if (len_self < 0) return NULL; len_other = PyObject_Size(other); if (len_other < 0) return NULL; ok = 0; switch(op) { case Py_NE: case Py_EQ: if (len_self == len_other) ok = all_contained_in(self, other); if (op == Py_NE && ok >= 0) ok = !ok; break; case Py_LT: if (len_self < len_other) ok = all_contained_in(self, other); break; case Py_LE: if (len_self <= len_other) ok = all_contained_in(self, other); break; case Py_GT: if (len_self > len_other) ok = all_contained_in(other, self); break; case Py_GE: if (len_self >= len_other) ok = all_contained_in(other, self); break; } if (ok < 0) return NULL; result = ok ? Py_True : Py_False; Py_INCREF(result); return result; } static PyObject * dictview_repr(_PyDictViewObject *dv) { PyObject *seq; PyObject *result = NULL; Py_ssize_t rc; rc = Py_ReprEnter((PyObject *)dv); if (rc != 0) { return rc > 0 ? PyUnicode_FromString("...") : NULL; } seq = PySequence_List((PyObject *)dv); if (seq == NULL) { goto Done; } result = PyUnicode_FromFormat("%s(%R)", Py_TYPE(dv)->tp_name, seq); Py_DECREF(seq); Done: Py_ReprLeave((PyObject *)dv); return result; } /*** dict_keys ***/ static PyObject * dictkeys_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictIterKey_Type); } static int dictkeys_contains(_PyDictViewObject *dv, PyObject *obj) { if (dv->dv_dict == NULL) return 0; return PyDict_Contains((PyObject *)dv->dv_dict, obj); } static PySequenceMethods dictkeys_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictkeys_contains, /* sq_contains */ }; // Create an set object from dictviews object. // Returns a new reference. // This utility function is used by set operations. static PyObject* dictviews_to_set(PyObject *self) { PyObject *left = self; if (PyDictKeys_Check(self)) { // PySet_New() has fast path for the dict object. PyObject *dict = (PyObject *)((_PyDictViewObject *)self)->dv_dict; if (PyDict_CheckExact(dict)) { left = dict; } } return PySet_New(left); } static PyObject* dictviews_sub(PyObject *self, PyObject *other) { PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } _Py_IDENTIFIER(difference_update); PyObject *tmp = _PyObject_CallMethodIdOneArg( result, &PyId_difference_update, other); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static int dictitems_contains(_PyDictViewObject *dv, PyObject *obj); PyObject * _PyDictView_Intersect(PyObject* self, PyObject *other) { PyObject *result; PyObject *it; PyObject *key; Py_ssize_t len_self; int rv; int (*dict_contains)(_PyDictViewObject *, PyObject *); /* Python interpreter swaps parameters when dict view is on right side of & */ if (!PyDictViewSet_Check(self)) { PyObject *tmp = other; other = self; self = tmp; } len_self = dictview_len((_PyDictViewObject *)self); /* if other is a set and self is smaller than other, reuse set intersection logic */ if (PySet_CheckExact(other) && len_self <= PyObject_Size(other)) { _Py_IDENTIFIER(intersection); return _PyObject_CallMethodIdObjArgs(other, &PyId_intersection, self, NULL); } /* if other is another dict view, and it is bigger than self, swap them */ if (PyDictViewSet_Check(other)) { Py_ssize_t len_other = dictview_len((_PyDictViewObject *)other); if (len_other > len_self) { PyObject *tmp = other; other = self; self = tmp; } } /* at this point, two things should be true 1. self is a dictview 2. if other is a dictview then it is smaller than self */ result = PySet_New(NULL); if (result == NULL) return NULL; it = PyObject_GetIter(other); if (it == NULL) { Py_DECREF(result); return NULL; } if (PyDictKeys_Check(self)) { dict_contains = dictkeys_contains; } /* else PyDictItems_Check(self) */ else { dict_contains = dictitems_contains; } while ((key = PyIter_Next(it)) != NULL) { rv = dict_contains((_PyDictViewObject *)self, key); if (rv < 0) { goto error; } if (rv) { if (PySet_Add(result, key)) { goto error; } } Py_DECREF(key); } Py_DECREF(it); if (PyErr_Occurred()) { Py_DECREF(result); return NULL; } return result; error: Py_DECREF(it); Py_DECREF(result); Py_DECREF(key); return NULL; } static PyObject* dictviews_or(PyObject* self, PyObject *other) { PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } if (_PySet_Update(result, other) < 0) { Py_DECREF(result); return NULL; } return result; } static PyObject * dictitems_xor(PyObject *self, PyObject *other) { assert(PyDictItems_Check(self)); assert(PyDictItems_Check(other)); PyObject *d1 = (PyObject *)((_PyDictViewObject *)self)->dv_dict; PyObject *d2 = (PyObject *)((_PyDictViewObject *)other)->dv_dict; PyObject *temp_dict = PyDict_Copy(d1); if (temp_dict == NULL) { return NULL; } PyObject *result_set = PySet_New(NULL); if (result_set == NULL) { Py_CLEAR(temp_dict); return NULL; } PyObject *key = NULL, *val1 = NULL, *val2 = NULL; Py_ssize_t pos = 0; Py_hash_t hash; while (_PyDict_Next(d2, &pos, &key, &val2, &hash)) { Py_INCREF(key); Py_INCREF(val2); val1 = _PyDict_GetItem_KnownHash(temp_dict, key, hash); int to_delete; if (val1 == NULL) { if (PyErr_Occurred()) { goto error; } to_delete = 0; } else { Py_INCREF(val1); to_delete = PyObject_RichCompareBool(val1, val2, Py_EQ); if (to_delete < 0) { goto error; } } if (to_delete) { if (_PyDict_DelItem_KnownHash(temp_dict, key, hash) < 0) { goto error; } } else { PyObject *pair = PyTuple_Pack(2, key, val2); if (pair == NULL) { goto error; } if (PySet_Add(result_set, pair) < 0) { Py_DECREF(pair); goto error; } Py_DECREF(pair); } Py_DECREF(key); Py_XDECREF(val1); Py_DECREF(val2); } key = val1 = val2 = NULL; _Py_IDENTIFIER(items); PyObject *remaining_pairs = _PyObject_CallMethodIdNoArgs(temp_dict, &PyId_items); if (remaining_pairs == NULL) { goto error; } if (_PySet_Update(result_set, remaining_pairs) < 0) { Py_DECREF(remaining_pairs); goto error; } Py_DECREF(temp_dict); Py_DECREF(remaining_pairs); return result_set; error: Py_XDECREF(temp_dict); Py_XDECREF(result_set); Py_XDECREF(key); Py_XDECREF(val1); Py_XDECREF(val2); return NULL; } static PyObject* dictviews_xor(PyObject* self, PyObject *other) { if (PyDictItems_Check(self) && PyDictItems_Check(other)) { return dictitems_xor(self, other); } PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } _Py_IDENTIFIER(symmetric_difference_update); PyObject *tmp = _PyObject_CallMethodIdOneArg( result, &PyId_symmetric_difference_update, other); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static PyNumberMethods dictviews_as_number = { 0, /*nb_add*/ (binaryfunc)dictviews_sub, /*nb_subtract*/ 0, /*nb_multiply*/ 0, /*nb_remainder*/ 0, /*nb_divmod*/ 0, /*nb_power*/ 0, /*nb_negative*/ 0, /*nb_positive*/ 0, /*nb_absolute*/ 0, /*nb_bool*/ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ (binaryfunc)_PyDictView_Intersect, /*nb_and*/ (binaryfunc)dictviews_xor, /*nb_xor*/ (binaryfunc)dictviews_or, /*nb_or*/ }; static PyObject* dictviews_isdisjoint(PyObject *self, PyObject *other) { PyObject *it; PyObject *item = NULL; if (self == other) { if (dictview_len((_PyDictViewObject *)self) == 0) Py_RETURN_TRUE; else Py_RETURN_FALSE; } /* Iterate over the shorter object (only if other is a set, * because PySequence_Contains may be expensive otherwise): */ if (PyAnySet_Check(other) || PyDictViewSet_Check(other)) { Py_ssize_t len_self = dictview_len((_PyDictViewObject *)self); Py_ssize_t len_other = PyObject_Size(other); if (len_other == -1) return NULL; if ((len_other > len_self)) { PyObject *tmp = other; other = self; self = tmp; } } it = PyObject_GetIter(other); if (it == NULL) return NULL; while ((item = PyIter_Next(it)) != NULL) { int contains = PySequence_Contains(self, item); Py_DECREF(item); if (contains == -1) { Py_DECREF(it); return NULL; } if (contains) { Py_DECREF(it); Py_RETURN_FALSE; } } Py_DECREF(it); if (PyErr_Occurred()) return NULL; /* PyIter_Next raised an exception. */ Py_RETURN_TRUE; } PyDoc_STRVAR(isdisjoint_doc, "Return True if the view and the given iterable have a null intersection."); static PyObject* dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_keys_doc, "Return a reverse iterator over the dict keys."); static PyMethodDef dictkeys_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {"__reversed__", (PyCFunction)(void(*)(void))dictkeys_reversed, METH_NOARGS, reversed_keys_doc}, {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictKeys_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_keys", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictkeys_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dictkeys_iter, /* tp_iter */ 0, /* tp_iternext */ dictkeys_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * dictkeys_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _PyDictView_New(dict, &PyDictKeys_Type); } static PyObject * dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterKey_Type); } /*** dict_items ***/ static PyObject * dictitems_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictIterItem_Type); } static int dictitems_contains(_PyDictViewObject *dv, PyObject *obj) { int result; PyObject *key, *value, *found; if (dv->dv_dict == NULL) return 0; if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) return 0; key = PyTuple_GET_ITEM(obj, 0); value = PyTuple_GET_ITEM(obj, 1); found = PyDict_GetItemWithError((PyObject *)dv->dv_dict, key); if (found == NULL) { if (PyErr_Occurred()) return -1; return 0; } Py_INCREF(found); result = PyObject_RichCompareBool(found, value, Py_EQ); Py_DECREF(found); return result; } static PySequenceMethods dictitems_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictitems_contains, /* sq_contains */ }; static PyObject* dictitems_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_items_doc, "Return a reverse iterator over the dict items."); static PyMethodDef dictitems_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {"__reversed__", (PyCFunction)dictitems_reversed, METH_NOARGS, reversed_items_doc}, {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictItems_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_items", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictitems_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dictitems_iter, /* tp_iter */ 0, /* tp_iternext */ dictitems_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * dictitems_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _PyDictView_New(dict, &PyDictItems_Type); } static PyObject * dictitems_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterItem_Type); } /*** dict_values ***/ static PyObject * dictvalues_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictIterValue_Type); } static PySequenceMethods dictvalues_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)0, /* sq_contains */ }; static PyObject* dictvalues_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_values_doc, "Return a reverse iterator over the dict values."); static PyMethodDef dictvalues_methods[] = { {"__reversed__", (PyCFunction)dictvalues_reversed, METH_NOARGS, reversed_values_doc}, {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictValues_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_values", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ 0, /* tp_as_number */ &dictvalues_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dictvalues_iter, /* tp_iter */ 0, /* tp_iternext */ dictvalues_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * dictvalues_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _PyDictView_New(dict, &PyDictValues_Type); } static PyObject * dictvalues_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterValue_Type); } /* Returns NULL if cannot allocate a new PyDictKeysObject, but does not set an error */ PyDictKeysObject * _PyDict_NewKeysForClass(void) { PyDictKeysObject *keys = new_keys_object(PyDict_MINSIZE); if (keys == NULL) { PyErr_Clear(); } else { keys->dk_lookup = lookdict_split; } return keys; } #define CACHED_KEYS(tp) (((PyHeapTypeObject*)tp)->ht_cached_keys) PyObject * PyObject_GenericGetDict(PyObject *obj, void *context) { PyObject *dict, **dictptr = _PyObject_GetDictPtr(obj); if (dictptr == NULL) { PyErr_SetString(PyExc_AttributeError, "This object has no __dict__"); return NULL; } dict = *dictptr; if (dict == NULL) { PyTypeObject *tp = Py_TYPE(obj); if ((tp->tp_flags & Py_TPFLAGS_HEAPTYPE) && CACHED_KEYS(tp)) { dictkeys_incref(CACHED_KEYS(tp)); *dictptr = dict = new_dict_with_shared_keys(CACHED_KEYS(tp)); } else { *dictptr = dict = PyDict_New(); } } Py_XINCREF(dict); return dict; } int _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, PyObject *key, PyObject *value) { PyObject *dict; int res; PyDictKeysObject *cached; assert(dictptr != NULL); if ((tp->tp_flags & Py_TPFLAGS_HEAPTYPE) && (cached = CACHED_KEYS(tp))) { assert(dictptr != NULL); dict = *dictptr; if (dict == NULL) { dictkeys_incref(cached); dict = new_dict_with_shared_keys(cached); if (dict == NULL) return -1; *dictptr = dict; } if (value == NULL) { res = PyDict_DelItem(dict, key); // Since key sharing dict doesn't allow deletion, PyDict_DelItem() // always converts dict to combined form. if ((cached = CACHED_KEYS(tp)) != NULL) { CACHED_KEYS(tp) = NULL; dictkeys_decref(cached); } } else { int was_shared = (cached == ((PyDictObject *)dict)->ma_keys); res = PyDict_SetItem(dict, key, value); if (was_shared && (cached = CACHED_KEYS(tp)) != NULL && cached != ((PyDictObject *)dict)->ma_keys) { /* PyDict_SetItem() may call dictresize and convert split table * into combined table. In such case, convert it to split * table again and update type's shared key only when this is * the only dict sharing key with the type. * * This is to allow using shared key in class like this: * * class C: * def __init__(self): * # one dict resize happens * self.a, self.b, self.c = 1, 2, 3 * self.d, self.e, self.f = 4, 5, 6 * a = C() */ if (cached->dk_refcnt == 1) { CACHED_KEYS(tp) = make_keys_shared(dict); } else { CACHED_KEYS(tp) = NULL; } dictkeys_decref(cached); if (CACHED_KEYS(tp) == NULL && PyErr_Occurred()) return -1; } } } else { dict = *dictptr; if (dict == NULL) { dict = PyDict_New(); if (dict == NULL) return -1; *dictptr = dict; } if (value == NULL) { res = PyDict_DelItem(dict, key); } else { res = PyDict_SetItem(dict, key, value); } } return res; } void _PyDictKeys_DecRef(PyDictKeysObject *keys) { dictkeys_decref(keys); } python-frozendict/src/frozendict/c_src/3_10/cpython_src/Objects/stringlib/000077500000000000000000000000001470250572200271625ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_10/cpython_src/Objects/stringlib/eq.h000066400000000000000000000015201470250572200277360ustar00rootroot00000000000000/* Fast unicode equal function optimized for dictobject.c and setobject.c */ /* Return 1 if two unicode objects are equal, 0 if not. * unicode_eq() is called when the hash of two unicode objects is equal. */ Py_LOCAL_INLINE(int) unicode_eq(PyObject *aa, PyObject *bb) { assert(PyUnicode_Check(aa)); assert(PyUnicode_Check(bb)); assert(PyUnicode_IS_READY(aa)); assert(PyUnicode_IS_READY(bb)); PyUnicodeObject *a = (PyUnicodeObject *)aa; PyUnicodeObject *b = (PyUnicodeObject *)bb; if (PyUnicode_GET_LENGTH(a) != PyUnicode_GET_LENGTH(b)) return 0; if (PyUnicode_GET_LENGTH(a) == 0) return 1; if (PyUnicode_KIND(a) != PyUnicode_KIND(b)) return 0; return memcmp(PyUnicode_1BYTE_DATA(a), PyUnicode_1BYTE_DATA(b), PyUnicode_GET_LENGTH(a) * PyUnicode_KIND(a)) == 0; } python-frozendict/src/frozendict/c_src/3_10/cpython_src/other.c000066400000000000000000000012421470250572200250600ustar00rootroot00000000000000static const unsigned int BitLengthTable[32] = { 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }; unsigned int _Py_bit_length(unsigned long d) { unsigned int d_bits = 0; while (d >= 32) { d_bits += 6; d >>= 6; } d_bits += BitLengthTable[d]; return d_bits; } typedef struct { uintptr_t _gc_next; uintptr_t _gc_prev; } PyGC_Head; #define _Py_AS_GC(o) ((PyGC_Head *)(o)-1) #define _PyObject_GC_IS_TRACKED(o) (_Py_AS_GC(o)->_gc_next != 0) #define _PyObject_GC_MAY_BE_TRACKED(obj) \ (PyObject_IS_GC(obj) && \ (!PyTuple_CheckExact(obj) || _PyObject_GC_IS_TRACKED(obj))) python-frozendict/src/frozendict/c_src/3_10/frozendictobject.c000066400000000000000000001656601470250572200247610ustar00rootroot00000000000000#include #include "frozendictobject.h" static PyObject* frozendict_iter(PyDictObject *dict); static int frozendict_equal(PyDictObject* a, PyDictObject* b); #include "other.c" #include "dictobject.c" static void frozendict_free_keys_object(PyDictKeysObject *keys, const int decref_items) { if (decref_items) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n; for (i = 0, n = keys->dk_nentries; i < n; i++) { Py_XDECREF(entries[i].me_key); Py_XDECREF(entries[i].me_value); } } PyObject_Free(keys); } static inline void frozendict_keys_decref(PyDictKeysObject *dk, const int decref_items) { assert(dk->dk_refcnt > 0); #ifdef Py_REF_DEBUG _Py_RefTotal--; #endif if (--dk->dk_refcnt == 0) { frozendict_free_keys_object(dk, decref_items); } } static int frozendict_resize(PyDictObject* mp, Py_ssize_t minsize) { const Py_ssize_t newsize = calculate_keysize(minsize); if (newsize <= 0) { PyErr_NoMemory(); return -1; } assert(IS_POWER_OF_2(newsize)); assert(newsize >= PyDict_MINSIZE); PyDictKeysObject* oldkeys = mp->ma_keys; /* Allocate a new table. */ PyDictKeysObject* new_keys = new_keys_object(newsize); if (new_keys == NULL) { return -1; } // New table must be large enough. assert(new_keys->dk_usable >= mp->ma_used); new_keys->dk_lookup = oldkeys->dk_lookup; const Py_ssize_t numentries = mp->ma_used; PyDictKeyEntry* newentries = DK_ENTRIES(new_keys); memcpy( newentries, DK_ENTRIES(oldkeys), numentries * sizeof(PyDictKeyEntry) ); build_indices(new_keys, newentries, numentries); new_keys->dk_usable -= numentries; new_keys->dk_nentries = numentries; // do not decref the keys inside! frozendict_keys_decref(oldkeys, 0); mp->ma_keys = new_keys; return 0; } static int frozendict_insert(PyDictObject *mp, PyObject *key, const Py_hash_t hash, PyObject *value, int empty) { Py_ssize_t ix; PyObject *old_value; PyDictKeysObject* keys = mp->ma_keys; Py_INCREF(key); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); if (! empty) { ix = keys->dk_lookup(mp, key, hash, &old_value); if (ix == DKIX_ERROR) { Py_DECREF(value); Py_DECREF(key); return -1; } empty = (ix == DKIX_EMPTY); } if (empty) { /* Insert into new slot. */ if (mp->ma_keys->dk_usable <= 0) { /* Need to resize. */ if (frozendict_resize(mp, GROWTH_RATE(mp))) { Py_DECREF(value); Py_DECREF(key); return -1; } // resize changes keys keys = mp->ma_keys; } const Py_ssize_t hashpos = find_empty_slot(keys, hash); const Py_ssize_t dk_nentries = keys->dk_nentries; PyDictKeyEntry* ep = &DK_ENTRIES(keys)[dk_nentries]; dictkeys_set_index(keys, hashpos, dk_nentries); ep->me_key = key; ep->me_hash = hash; ep->me_value = value; mp->ma_used++; keys->dk_usable--; keys->dk_nentries++; assert(keys->dk_usable >= 0); } else { DK_ENTRIES(mp->ma_keys)[ix].me_value = value; Py_DECREF(old_value); /* which **CAN** re-enter (see issue #22653) */ Py_DECREF(key); } ASSERT_CONSISTENT(mp); return 0; } static int frozendict_setitem(PyObject *op, PyObject *key, PyObject *value, int empty) { Py_hash_t hash; assert(key); assert(value); if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { return -1; } } return frozendict_insert((PyDictObject*) op, key, hash, value, empty); } // int _PyFrozendict_SetItem(PyObject *op, // PyObject *key, // PyObject *value, // int empty) { // if (! PyAnyFrozenDict_Check(op)) { // PyErr_BadInternalCall(); // return -1; // } // return frozendict_setitem(op, key, value, empty); // } static PyObject* _frozendict_new( PyTypeObject* type, PyObject* args, PyObject* kwds, const int use_empty_frozendict ); static PyObject * frozendict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value) { PyObject *it; /* iter(iterable) */ PyObject *key; PyObject *d; int status; d = _frozendict_new(&PyFrozenDict_Type, NULL, NULL, 0); if (d == NULL) return NULL; Py_ssize_t size; PyDictObject *mp = (PyDictObject *)d; mp->ma_keys = new_keys_object(PyDict_MINSIZE); if (PyAnyDict_CheckExact(iterable)) { PyObject *oldvalue; Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; size = PyDict_GET_SIZE(iterable); if (mp->ma_keys->dk_usable < size) { if (frozendict_resize(mp, estimate_keysize(size))) { Py_DECREF(d); return NULL; } } while (_d_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) { if (frozendict_insert(mp, key, hash, value, 0)) { Py_DECREF(d); return NULL; } } return d; } else if (PyAnySet_CheckExact(iterable)) { Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; size = PySet_GET_SIZE(iterable); if (mp->ma_keys->dk_usable < size) { if (frozendict_resize(mp, estimate_keysize(size))) { Py_DECREF(d); return NULL; } } while (_PySet_NextEntry(iterable, &pos, &key, &hash)) { if (frozendict_insert(mp, key, hash, value, 0)) { Py_DECREF(d); return NULL; } } } else { it = PyObject_GetIter(iterable); if (it == NULL){ Py_DECREF(d); return NULL; } while ((key = PyIter_Next(it)) != NULL) { status = frozendict_setitem(d, key, value, 0); Py_DECREF(key); if (status < 0) { Py_DECREF(it); Py_DECREF(d); return NULL; } } Py_DECREF(it); if (PyErr_Occurred()) { Py_DECREF(d); return NULL; } } ASSERT_CONSISTENT(mp); if (type == &PyFrozenDict_Type) { return d; } PyObject* args = PyTuple_New(1); if (args == NULL) { Py_DECREF(d); return NULL; } PyTuple_SET_ITEM(args, 0, d); PyObject* res = PyObject_Call((PyObject*) type, args, NULL); Py_DECREF(args); return res; } /* Methods */ #define REPR_GENERIC_START "(" #define REPR_GENERIC_END ")" #define REPR_GENERIC_START_LEN 1 #define REPR_GENERIC_END_LEN 1 static PyObject* frozendict_repr(PyFrozenDictObject* mp) { PyObject* dict_repr_res = dict_repr((PyDictObject*) mp); if (dict_repr_res == NULL) { return NULL; } _PyUnicodeWriter writer; _PyUnicodeWriter_Init(&writer); int error = 0; PyObject* o = (PyObject*) mp; Py_ReprEnter(o); PyTypeObject* type = Py_TYPE(mp); size_t frozendict_name_len = strlen(type->tp_name); writer.min_length = ( frozendict_name_len + REPR_GENERIC_START_LEN + PyObject_Length(dict_repr_res) + REPR_GENERIC_END_LEN ); if (_PyUnicodeWriter_WriteASCIIString( &writer, type->tp_name, frozendict_name_len )) { error = 1; } else { if (_PyUnicodeWriter_WriteASCIIString( &writer, REPR_GENERIC_START, REPR_GENERIC_START_LEN )) { error = 1; } else { if (_PyUnicodeWriter_WriteStr(&writer, dict_repr_res)) { error = 1; } else { error = _PyUnicodeWriter_WriteASCIIString( &writer, REPR_GENERIC_END, REPR_GENERIC_END_LEN ); } } } Py_ReprLeave(o); Py_DECREF(dict_repr_res); if (error) { _PyUnicodeWriter_Dealloc(&writer); return NULL; } return _PyUnicodeWriter_Finish(&writer); } static PyMappingMethods frozendict_as_mapping = { (lenfunc)dict_length, /*mp_length*/ (binaryfunc)dict_subscript, /*mp_subscript*/ }; static int frozendict_merge(PyObject* a, PyObject* b, int empty) { /* We accept for the argument either a concrete dictionary object, * or an abstract "mapping" object. For the former, we can do * things quite efficiently. For the latter, we only require that * PyMapping_Keys() and PyObject_GetItem() be supported. */ assert(a != NULL); assert(PyAnyFrozenDict_Check(a)); assert(b != NULL); PyDictObject* mp = (PyDictObject*) a; if ( PyAnyDict_Check(b) && ( Py_TYPE(b)->tp_iter == PyDict_Type.tp_iter || Py_TYPE(b)->tp_iter == (getiterfunc)frozendict_iter ) ) { PyDictObject* other = (PyDictObject*)b; const Py_ssize_t numentries = other->ma_used; if (other == mp || numentries == 0) { /* a.update(a) or a.update({}); nothing to do */ return 0; } const int is_other_combined = other->ma_values == NULL; PyDictKeysObject* okeys = other->ma_keys; if ( empty && is_other_combined && numentries == okeys->dk_nentries ) { PyDictKeysObject *keys = clone_combined_dict_keys(other); if (keys == NULL) { return -1; } mp->ma_keys = keys; mp->ma_used = numentries; mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(mp); if (_PyObject_GC_IS_TRACKED(other) && !_PyObject_GC_IS_TRACKED(mp)) { PyObject_GC_Track(mp); } return 0; } PyDictKeyEntry* ep0 = DK_ENTRIES(okeys); PyDictKeyEntry* entry; PyObject* key; PyObject* value; Py_hash_t hash; int err; if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } /* Do one big resize at the start, rather than * incrementally resizing as we insert new items. Expect * that there will be no (or few) overlapping keys. */ if (mp->ma_keys->dk_usable < numentries) { if (frozendict_resize(mp, estimate_keysize(mp->ma_used + numentries))) { return -1; } } for (Py_ssize_t i = 0, n = okeys->dk_nentries; i < n; i++) { entry = &ep0[i]; key = entry->me_key; hash = entry->me_hash; if (is_other_combined) { value = entry->me_value; } else { value = other->ma_values[i]; } if (value != NULL) { Py_INCREF(key); Py_INCREF(value); err = frozendict_insert(mp, key, hash, value, empty); Py_DECREF(value); Py_DECREF(key); if (err != 0) { return -1; } if (n != other->ma_keys->dk_nentries) { PyErr_SetString(PyExc_RuntimeError, "dict mutated during update"); return -1; } } } } else { /* Do it the generic, slower way */ PyObject *keys = PyMapping_Keys(b); PyObject *iter; PyObject *key, *value; int status; if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } if (keys == NULL) /* Docstring says this is equivalent to E.keys() so * if E doesn't have a .keys() method we want * AttributeError to percolate up. Might as well * do the same for any other error. */ return -1; iter = PyObject_GetIter(keys); Py_DECREF(keys); if (iter == NULL) return -1; for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) { value = PyObject_GetItem(b, key); if (value == NULL) { Py_DECREF(iter); Py_DECREF(key); return -1; } status = frozendict_setitem(a, key, value, 0); Py_DECREF(key); Py_DECREF(value); if (status < 0) { Py_DECREF(iter); return -1; } } Py_DECREF(iter); if (PyErr_Occurred()) /* Iterator completed, via error */ return -1; } ASSERT_CONSISTENT(a); return 0; } static int frozendict_merge_from_seq2(PyObject* d, PyObject* seq2) { assert(d != NULL); assert(PyAnyFrozenDict_Check(d)); assert(seq2 != NULL); PyObject* it = PyObject_GetIter(seq2); if (it == NULL) { return -1; } PyObject* fast; /* item as a 2-tuple or 2-list */ PyObject* key = NULL; PyObject* value = NULL; Py_ssize_t n; PyObject* item; int res = 0; PyDictObject* mp = (PyDictObject*) d; if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } for (Py_ssize_t i = 0; ; ++i) { fast = NULL; item = PyIter_Next(it); if (item == NULL) { if (PyErr_Occurred()) { res = -1; } break; } /* Convert item to sequence, and verify length 2. */ fast = PySequence_Fast(item, ""); if (fast == NULL) { if (PyErr_ExceptionMatches(PyExc_TypeError)) { PyErr_Format(PyExc_TypeError, "cannot convert dictionary update " "sequence element #%zd to a sequence", i); } Py_DECREF(item); res = -1; break; } n = PySequence_Fast_GET_SIZE(fast); if (n != 2) { PyErr_Format(PyExc_ValueError, "dictionary update sequence element #%zd " "has length %zd; 2 is required", i, n); Py_DECREF(fast); Py_DECREF(item); res = -1; break; } /* Update/merge with this (key, value) pair. */ key = PySequence_Fast_GET_ITEM(fast, 0); Py_INCREF(key); value = PySequence_Fast_GET_ITEM(fast, 1); Py_INCREF(value); if (frozendict_setitem(d, key, value, 0) < 0) { Py_DECREF(key); Py_DECREF(value); Py_DECREF(fast); Py_DECREF(item); res = -1; break; } Py_DECREF(key); Py_DECREF(value); Py_DECREF(fast); Py_DECREF(item); } Py_DECREF(it); ASSERT_CONSISTENT(d); return res; } static int frozendict_update_arg(PyObject *self, PyObject *arg, const int empty) { if (PyAnyDict_CheckExact(arg)) { return frozendict_merge(self, arg, empty); } _Py_IDENTIFIER(keys); PyObject *func; if (_PyObject_LookupAttrId(arg, &PyId_keys, &func) < 0) { return -1; } if (func != NULL) { Py_DECREF(func); return frozendict_merge(self, arg, empty); } return frozendict_merge_from_seq2(self, arg); } static int frozendict_update_common(PyObject* self, PyObject* arg, PyObject* kwds) { int result = 0; const int no_arg = (arg == NULL); if (! no_arg) { result = frozendict_update_arg(self, arg, 1); } if (result == 0 && kwds != NULL) { if (PyArg_ValidateKeywordArguments(kwds)) { result = frozendict_merge(self, kwds, no_arg); } else { result = -1; } } return result; } /* Forward */ static PyObject *frozendictkeys_new(PyObject *, PyObject *); static PyObject *frozendictitems_new(PyObject *, PyObject *); static PyObject *frozendictvalues_new(PyObject *, PyObject *); #define MINUSONE_HASH ((Py_hash_t) -1) static Py_hash_t frozendict_hash(PyObject* self) { PyFrozenDictObject* frozen_self = (PyFrozenDictObject*) self; Py_hash_t hash; if (frozen_self->ma_hash != MINUSONE_HASH) { hash = frozen_self->ma_hash; } else { PyObject* frozen_items_tmp = frozendictitems_new(self, NULL); int save_hash = 1; if (frozen_items_tmp == NULL) { hash = MINUSONE_HASH; save_hash = 0; } else { PyObject* frozen_items = PyFrozenSet_New(frozen_items_tmp); Py_DECREF(frozen_items_tmp); if (frozen_items == NULL) { hash = MINUSONE_HASH; save_hash = 0; } else { hash = PyFrozenSet_Type.tp_hash(frozen_items); Py_DECREF(frozen_items); } } if (save_hash) { frozen_self->ma_hash = hash; } } return hash; } static PyObject* frozendict_copy(PyObject* o, PyObject* Py_UNUSED(ignored)) { if (PyAnyFrozenDict_CheckExact(o)) { Py_INCREF(o); return o; } PyObject* args = PyTuple_New(1); if (args == NULL) { return NULL; } Py_INCREF(o); PyTuple_SET_ITEM(args, 0, o); PyTypeObject* type = Py_TYPE(o); PyObject* res = PyObject_Call((PyObject *) type, args, NULL); Py_DECREF(args); return res; } PyObject* frozendict_deepcopy(PyObject* self, PyObject* memo) { if (PyAnyFrozenDict_CheckExact(self)) { frozendict_hash(self); if (PyErr_Occurred()) { PyErr_Clear(); } else { Py_INCREF(self); return self; } } if (! PyAnyFrozenDict_Check(self)) { Py_RETURN_NOTIMPLEMENTED; } PyObject* d = PyDict_New(); if (d == NULL) { return NULL; } PyObject* copy_module_name = NULL; PyObject* copy_module = NULL; PyObject* deepcopy_fun = NULL; PyObject* deep_args = NULL; PyObject* deep_d = NULL; PyObject* args = NULL; PyObject* res = NULL; int decref_d = 1; int decref_deep_d = 1; if (PyDict_Merge(d, self, 1)) { goto end; } copy_module_name = PyUnicode_FromString("copy"); if (copy_module_name == NULL) { goto end; } copy_module = PyImport_Import(copy_module_name); if (copy_module == NULL) { goto end; } deepcopy_fun = PyObject_GetAttrString(copy_module, "deepcopy"); if (deepcopy_fun == NULL) { goto end; } deep_args = PyTuple_New(2); if (deep_args == NULL) { goto end; } PyTuple_SET_ITEM(deep_args, 0, d); decref_d = 0; Py_INCREF(memo); PyTuple_SET_ITEM(deep_args, 1, memo); deep_d = PyObject_CallObject(deepcopy_fun, deep_args); if (deep_d == NULL) { goto end; } args = PyTuple_New(1); if (args == NULL) { goto end; } PyTuple_SET_ITEM(args, 0, deep_d); decref_deep_d = 0; PyTypeObject* type = Py_TYPE(self); res = PyObject_Call((PyObject *) type, args, NULL); end: Py_XDECREF(args); Py_XDECREF(deep_args); Py_XDECREF(deepcopy_fun); Py_XDECREF(copy_module); Py_XDECREF(copy_module_name); if (decref_d) { Py_DECREF(d); } if (decref_deep_d) { Py_DECREF(deep_d); } return res; } static int frozendict_equal(PyDictObject* a, PyDictObject* b) { if (a == b) { return 1; } if (a->ma_used != b->ma_used) { /* can't be equal if # of entries differ */ return 0; } PyDictKeysObject* keys = a->ma_keys; PyDictKeyEntry* ep; PyObject* aval; int cmp = 1; PyObject* bval; PyObject* key; /* Same # of entries -- check all of 'em. Exit early on any diff. */ for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) { ep = &DK_ENTRIES(keys)[i]; aval = ep->me_value; Py_INCREF(aval); key = ep->me_key; Py_INCREF(key); /* reuse the known hash value */ b->ma_keys->dk_lookup(b, key, ep->me_hash, &bval); if (bval == NULL) { if (PyErr_Occurred()) { cmp = -1; } else { cmp = 0; } } else { Py_INCREF(bval); cmp = PyObject_RichCompareBool(aval, bval, Py_EQ); Py_DECREF(bval); } Py_DECREF(key); Py_DECREF(aval); if (cmp <= 0) { /* error or not equal */ break; } } return cmp; } static Py_ssize_t dict_get_index(PyDictObject *self, PyObject *key) { Py_hash_t hash; PyObject* val; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return DKIX_ERROR; } return (self->ma_keys->dk_lookup) (self, key, hash, &val); } static PyObject * frozendict_reduce(PyFrozenDictObject* mp, PyObject *Py_UNUSED(ignored)) { PyObject *d = PyDict_New(); if (d == NULL) { return NULL; } if (PyDict_Merge(d, (PyObject *)mp, 1)) { Py_DECREF(d); return NULL; } return Py_BuildValue("O(N)", Py_TYPE(mp), d); } static PyObject* frozendict_clone(PyObject* self) { PyTypeObject* type = Py_TYPE(self); PyObject* new_op = type->tp_alloc(type, 0); if (new_op == NULL){ return NULL; } if (type == &PyFrozenDict_Type) { PyObject_GC_UnTrack(new_op); } PyDictObject* mp = (PyDictObject*) self; PyDictKeysObject *keys = clone_combined_dict_keys(mp); if (keys == NULL) { return NULL; } PyFrozenDictObject* new_mp = (PyFrozenDictObject*) new_op; new_mp->ma_keys = keys; if (_PyObject_GC_IS_TRACKED(mp) && !_PyObject_GC_IS_TRACKED(new_mp)) { PyObject_GC_Track(new_mp); } new_mp->ma_used = mp->ma_used; new_mp->ma_hash = MINUSONE_HASH; new_mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(new_mp); return new_op; } static PyObject* frozendict_set( PyObject* self, PyObject* const* args, Py_ssize_t nargs ) { if (!_PyArg_CheckPositional("set", nargs, 2, 2)) { return NULL; } PyObject* new_op = frozendict_clone(self); if (new_op == NULL) { return NULL; } PyObject* set_key = args[0]; if (frozendict_setitem(new_op, set_key, args[1], 0)) { Py_DECREF(new_op); return NULL; } if ( ((PyDictObject*) self)->ma_keys->dk_lookup == lookdict_unicode_nodummy && ! PyUnicode_CheckExact(set_key) ) { ((PyFrozenDictObject*) new_op)->ma_keys->dk_lookup = lookdict; } return new_op; } static PyObject* frozendict_setdefault( PyObject* self, PyObject* const* args, Py_ssize_t nargs ) { if (!_PyArg_CheckPositional("setdefault", nargs, 1, 2)) { return NULL; } PyObject* set_key = args[0]; if (PyDict_Contains(self, set_key)) { Py_INCREF(self); return self; } PyObject* new_op = frozendict_clone(self); if (new_op == NULL) { return NULL; } PyObject* val; if (nargs == 2) { val = args[1]; } else { val = Py_None; } if (frozendict_setitem(new_op, set_key, val, 0)) { Py_DECREF(new_op); return NULL; } if ( ((PyDictObject*) self)->ma_keys->dk_lookup == lookdict_unicode_nodummy && ! PyUnicode_CheckExact(set_key) ) { ((PyFrozenDictObject*) new_op)->ma_keys->dk_lookup = lookdict; } return new_op; } static PyObject* frozendict_delete( PyObject* self, PyObject *const *args, Py_ssize_t nargs ) { if (! _PyArg_CheckPositional("delete", nargs, 1, 1)) { return NULL; } PyDictObject* mp = (PyDictObject*) self; PyObject* del_key = args[0]; const Py_ssize_t ix = dict_get_index(mp, del_key); if (ix == DKIX_ERROR) { return NULL; } if (ix == DKIX_EMPTY) { _PyErr_SetKeyError(del_key); return NULL; } const Py_ssize_t size = mp->ma_used; const Py_ssize_t sizemm = size - 1; if (sizemm == 0) { PyObject* args = PyTuple_New(0); if (args == NULL) { return NULL; } return PyObject_Call((PyObject*) Py_TYPE(self), args, NULL); } PyTypeObject* type = Py_TYPE(self); PyObject* new_op = type->tp_alloc(type, 0); if (new_op == NULL) { return NULL; } if (type == &PyFrozenDict_Type) { PyObject_GC_UnTrack(new_op); } const Py_ssize_t newsize = estimate_keysize(sizemm); if (newsize <= 0) { Py_DECREF(new_op); PyErr_NoMemory(); return NULL; } assert(IS_POWER_OF_2(newsize)); assert(newsize >= PyDict_MINSIZE); /* Allocate a new table. */ PyDictKeysObject* new_keys = new_keys_object(newsize); if (new_keys == NULL) { Py_DECREF(new_op); return NULL; } const PyDictKeysObject* old_keys = mp->ma_keys; new_keys->dk_lookup = old_keys->dk_lookup; PyFrozenDictObject* new_mp = (PyFrozenDictObject*) new_op; // New table must be large enough. assert(new_keys->dk_usable >= new_mp->ma_used); new_mp->ma_keys = new_keys; new_mp->ma_hash = MINUSONE_HASH; new_mp->ma_version_tag = DICT_NEXT_VERSION(); PyObject* key; PyObject* value; Py_hash_t hash; Py_ssize_t hashpos; PyDictKeyEntry* old_entries = DK_ENTRIES(old_keys); PyDictKeyEntry* new_entries = DK_ENTRIES(new_keys); PyDictKeyEntry* old_entry; PyDictKeyEntry* new_entry; Py_ssize_t new_i; int deleted = 0; for (Py_ssize_t i = 0; i < size; i++) { if (i == ix) { deleted = 1; continue; } new_i = i - deleted; old_entry = &old_entries[i]; hash = old_entry->me_hash; key = old_entry->me_key; value = old_entry->me_value; Py_INCREF(key); Py_INCREF(value); hashpos = find_empty_slot(new_keys, hash); dictkeys_set_index(new_keys, hashpos, new_i); new_entry = &new_entries[new_i]; new_entry->me_key = key; new_entry->me_hash = hash; new_entry->me_value = value; } new_mp->ma_used = sizemm; new_keys->dk_usable -= sizemm; new_keys->dk_nentries = sizemm; ASSERT_CONSISTENT(new_mp); return new_op; } static const PyObject* frozendict_key( PyObject* self, PyObject *const *args, Py_ssize_t nargs ) { if (! _PyArg_CheckPositional("key", nargs, 0, 1)) { return NULL; } Py_ssize_t index; Py_ssize_t passed_index; PyDictObject* d = (PyDictObject*) self; const Py_ssize_t size = d->ma_used; if (nargs > 0) { index = PyLong_AsSsize_t(args[0]); passed_index = index; if (index < 0) { if (PyErr_Occurred()) { return NULL; } index += size; } } else { index = 0; passed_index = index; } const Py_ssize_t maxindex = size - 1; if (index > maxindex || index < 0) { PyErr_Format( PyExc_IndexError, "%s index %zd out of range %zd", Py_TYPE(self)->tp_name, passed_index, maxindex ); return NULL; } const PyObject* res = DK_ENTRIES(d->ma_keys)[index].me_key; Py_INCREF(res); return res; } static const PyObject* frozendict_value( PyObject* self, PyObject *const *args, Py_ssize_t nargs ) { if (! _PyArg_CheckPositional("value", nargs, 0, 1)) { return NULL; } Py_ssize_t index; Py_ssize_t passed_index; PyDictObject* d = (PyDictObject*) self; const Py_ssize_t size = d->ma_used; if (nargs > 0) { index = PyLong_AsSsize_t(args[0]); passed_index = index; if (index < 0) { if (PyErr_Occurred()) { return NULL; } index += size; } } else { index = 0; passed_index = index; } const Py_ssize_t maxindex = size - 1; if (index > maxindex || index < 0) { PyErr_Format( PyExc_IndexError, "%s index %zd out of range %zd", Py_TYPE(self)->tp_name, passed_index, maxindex ); return NULL; } const PyObject* res = DK_ENTRIES(d->ma_keys)[index].me_value; Py_INCREF(res); return res; } static const PyObject* frozendict_item( PyObject* self, PyObject *const *args, Py_ssize_t nargs ) { if (! _PyArg_CheckPositional("item", nargs, 0, 1)) { return NULL; } Py_ssize_t index; Py_ssize_t passed_index; PyDictObject* d = (PyDictObject*) self; const Py_ssize_t size = d->ma_used; if (nargs > 0) { index = PyLong_AsSsize_t(args[0]); passed_index = index; if (index < 0) { if (PyErr_Occurred()) { return NULL; } index += size; } } else { index = 0; passed_index = index; } const Py_ssize_t maxindex = size - 1; if (index > maxindex || index < 0) { PyErr_Format( PyExc_IndexError, "%s index %zd out of range %zd", Py_TYPE(self)->tp_name, passed_index, maxindex ); return NULL; } PyObject* key = DK_ENTRIES(d->ma_keys)[index].me_key; Py_INCREF(key); PyObject* val = DK_ENTRIES(d->ma_keys)[index].me_value; Py_INCREF(val); const PyObject* res = PyTuple_New(2); PyTuple_SET_ITEM(res, 0, key); PyTuple_SET_ITEM(res, 1, val); return res; } PyDoc_STRVAR(frozendict_set_doc, "set($self, key, value, /)\n" "--\n" "\n" "Returns a copy of the dictionary with the new (key, value) item. "); PyDoc_STRVAR(frozendict_setdefault_doc, "set($self, key[, default], /)\n" "--\n" "\n" "If key is in the dictionary, it returns the dictionary unchanged. \n" "Otherwise, it returns a copy of the dictionary with the new (key, default) item; \n" "default argument is optional and is None by default. "); PyDoc_STRVAR(frozendict_delete_doc, "delete($self, key, /)\n" "--\n" "\n" "Returns a copy of the dictionary without the item of the corresponding key. "); PyDoc_STRVAR(frozendict_key_doc, "key($self[, index], /)\n" "--\n" "\n" "Get the key at the specified index (insertion order). If index is not \n" "passed, it defaults to 0. If index is negative, returns the key at \n" "position size + index. "); PyDoc_STRVAR(frozendict_value_doc, "value($self[, index], /)\n" "--\n" "\n" "Get the value at the specified index (insertion order). If index is not \n" "passed, it defaults to 0. If index is negative, returns the value at \n" "position size + index. "); PyDoc_STRVAR(frozendict_item_doc, "item($self[, index], /)\n" "--\n" "\n" "Get the (key, value) item at the specified index (insertion order). If \n" "index is not passed, it defaults to 0. If index is negative, returns the \n" "item at position size + index. "); static PyMethodDef frozendict_mapp_methods[] = { DICT___CONTAINS___METHODDEF {"__getitem__", (PyCFunction)(void(*)(void))dict_subscript, METH_O | METH_COEXIST, getitem__doc__}, {"__sizeof__", (PyCFunction)(void(*)(void))dict_sizeof, METH_NOARGS, sizeof__doc__}, DICT_GET_METHODDEF {"keys", frozendictkeys_new, METH_NOARGS, keys__doc__}, {"items", frozendictitems_new, METH_NOARGS, items__doc__}, {"values", frozendictvalues_new, METH_NOARGS, values__doc__}, {"fromkeys", (PyCFunction)(void(*)(void))dict_fromkeys, METH_FASTCALL|METH_CLASS, dict_fromkeys__doc__}, {"copy", (PyCFunction)frozendict_copy, METH_NOARGS, copy__doc__}, {"__copy__", (PyCFunction)frozendict_copy, METH_NOARGS, "Returns a copy of the object."}, {"__deepcopy__", (PyCFunction)frozendict_deepcopy, METH_O, "Returns a deepcopy of the object."}, DICT___REVERSED___METHODDEF {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, "See PEP 585"}, {"__reduce__", (PyCFunction)(void(*)(void))frozendict_reduce, METH_NOARGS, ""}, {"set", (PyCFunction)(void(*)(void)) frozendict_set, METH_FASTCALL, frozendict_set_doc}, {"setdefault", (PyCFunction)(void(*)(void)) frozendict_setdefault, METH_FASTCALL, frozendict_setdefault_doc}, {"delete", (PyCFunction)(void(*)(void)) frozendict_delete, METH_FASTCALL, frozendict_delete_doc}, {"key", (PyCFunction)(void(*)(void)) frozendict_key, METH_FASTCALL, frozendict_key_doc}, {"value", (PyCFunction)(void(*)(void)) frozendict_value, METH_FASTCALL, frozendict_value_doc}, {"item", (PyCFunction)(void(*)(void)) frozendict_item, METH_FASTCALL, frozendict_item_doc}, {NULL, NULL} /* sentinel */ }; static PyObject* frozendict_new_barebone(PyTypeObject* type) { PyObject* self = type->tp_alloc(type, 0); if (self == NULL) { return NULL; } /* The object has been implicitly tracked by tp_alloc */ if (type == &PyFrozenDict_Type) { PyObject_GC_UnTrack(self); } PyFrozenDictObject* mp = (PyFrozenDictObject*) self; mp->ma_keys = NULL; mp->ma_values = NULL; mp->ma_used = 0; mp->ma_hash = MINUSONE_HASH; return self; } // empty frozendict singleton static PyObject* empty_frozendict = NULL; // if frozendict is empty, return the empty singleton static PyObject* frozendict_create_empty( PyFrozenDictObject* mp, const PyTypeObject* type, const int use_empty_frozendict ) { if (mp->ma_used == 0) { if (use_empty_frozendict && type == &PyFrozenDict_Type) { if (empty_frozendict == NULL) { empty_frozendict = (PyObject*) mp; Py_INCREF(Py_EMPTY_KEYS); ((PyDictObject*) empty_frozendict)->ma_keys = Py_EMPTY_KEYS; mp->ma_version_tag = DICT_NEXT_VERSION(); } else { Py_DECREF(mp); } Py_INCREF(empty_frozendict); return empty_frozendict; } else { if (mp->ma_keys != NULL) { frozendict_keys_decref(mp->ma_keys, 0); } Py_INCREF(Py_EMPTY_KEYS); mp->ma_keys = Py_EMPTY_KEYS; return NULL; } } return NULL; } static PyObject* frozendict_vectorcall(PyObject* type, PyObject* const* args, size_t nargsf, PyObject* kwnames) { assert(PyType_Check(type)); Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); if (! _PyArg_CheckPositional("dict", nargs, 0, 1)) { return NULL; } PyTypeObject* ttype = (PyTypeObject*) type; Py_ssize_t size; PyObject* arg = NULL; if (nargs == 1) { arg = args[0]; // only argument is a frozendict if ( arg != NULL && PyAnyFrozenDict_CheckExact(arg) && ttype == &PyFrozenDict_Type ) { size = ( kwnames == NULL ? 0 : PyTuple_GET_SIZE(kwnames) ); if (size == 0) { Py_INCREF(arg); return arg; } } } PyObject* self = frozendict_new_barebone(ttype); PyFrozenDictObject* mp = (PyFrozenDictObject*) self; int empty = 1; if (nargs == 1) { empty = 0; if (frozendict_update_arg(self, arg, 1) < 0) { Py_DECREF(self); return NULL; } args++; } if (kwnames != NULL) { if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } size = ( kwnames == NULL ? 0 : PyTuple_GET_SIZE(kwnames) ); if (mp->ma_keys->dk_usable < size) { if (frozendict_resize((PyDictObject*) self, estimate_keysize(mp->ma_used + size))) { return NULL; } } for (Py_ssize_t i = 0; i < size; i++) { if (frozendict_setitem(self, PyTuple_GET_ITEM(kwnames, i), args[i], empty) < 0) { Py_DECREF(self); return NULL; } } } PyObject* self_empty = frozendict_create_empty(mp, ttype, 1); if (self_empty != NULL) { return self_empty; } mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(mp); return self; } static PyObject* _frozendict_new( PyTypeObject* type, PyObject* args, PyObject* kwds, const int use_empty_frozendict ) { assert(type != NULL && type->tp_alloc != NULL); PyObject* arg = NULL; if (args != NULL && ! PyArg_UnpackTuple(args, "dict", 0, 1, &arg)) { return NULL; } const int arg_is_frozendict = (arg != NULL && PyAnyFrozenDict_CheckExact(arg)); const int kwds_size = ((kwds != NULL) ? ((PyDictObject*) kwds)->ma_used : 0 ); // only argument is a frozendict if (arg_is_frozendict && kwds_size == 0 && type == &PyFrozenDict_Type) { Py_INCREF(arg); return arg; } PyObject* self = frozendict_new_barebone(type); PyFrozenDictObject* mp = (PyFrozenDictObject*) self; if (frozendict_update_common(self, arg, kwds)) { Py_DECREF(self); return NULL; } PyObject* empty = frozendict_create_empty(mp, type, use_empty_frozendict); if (empty != NULL) { return empty; } mp->ma_version_tag = DICT_NEXT_VERSION(); return self; } static PyObject* frozendict_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { return _frozendict_new(type, args, kwds, 1); } static PyObject* frozendict_or(PyObject *self, PyObject *other) { if (! PyAnyFrozenDict_Check(self) || ! PyAnyDict_Check(other)) { Py_RETURN_NOTIMPLEMENTED; } PyObject* new = frozendict_clone(self); if (new == NULL) { return NULL; } if (frozendict_update_arg(new, other, 0)) { Py_DECREF(new); return NULL; } return new; } static PyNumberMethods frozendict_as_number = { .nb_or = frozendict_or, }; #define FROZENDICT_CLASS_NAME "frozendict" #define FROZENDICT_MODULE_NAME "frozendict" #define FROZENDICT_FULL_NAME FROZENDICT_MODULE_NAME "." FROZENDICT_CLASS_NAME PyDoc_STRVAR(frozendict_doc, "An immutable version of dict.\n" "\n" FROZENDICT_FULL_NAME "() -> returns an empty immutable dictionary\n" FROZENDICT_FULL_NAME "(mapping) -> returns an immutable dictionary initialized from a mapping object's\n" " (key, value) pairs\n" FROZENDICT_FULL_NAME "(iterable) -> returns an immutable dictionary, equivalent to:\n" " d = {}\n" " " " for k, v in iterable:\n" " d[k] = v\n" " " " " FROZENDICT_FULL_NAME "(d)\n" FROZENDICT_FULL_NAME "(**kwargs) -> returns an immutable dictionary initialized with the name=value pairs\n" " in the keyword argument list. For example: " FROZENDICT_FULL_NAME "(one=1, two=2)"); static PyTypeObject PyFrozenDict_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_FULL_NAME, /* tp_name */ sizeof(PyFrozenDictObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)dict_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)frozendict_repr, /* tp_repr */ &frozendict_as_number, /* tp_as_number */ &dict_as_sequence, /* tp_as_sequence */ &frozendict_as_mapping, /* tp_as_mapping */ (hashfunc)frozendict_hash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | _Py_TPFLAGS_MATCH_SELF | Py_TPFLAGS_MAPPING, /* tp_flags */ frozendict_doc, /* tp_doc */ dict_traverse, /* tp_traverse */ 0, /* tp_clear */ dict_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendict_iter, /* tp_iter */ 0, /* tp_iternext */ frozendict_mapp_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ frozendict_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ .tp_vectorcall = frozendict_vectorcall, }; /* Dictionary iterator types */ static PyObject* frozendict_iter(PyDictObject *dict) { return dictiter_new(dict, &PyFrozenDictIterKey_Type); } static PyObject* frozendictiter_iternextkey(dictiterobject* di) { Py_ssize_t pos = di->di_pos; assert(pos >= 0); PyDictObject* d = di->di_dict; if (d == NULL) { return NULL; } assert(PyAnyFrozenDict_Check(d)); if (pos >= d->ma_used) { di->di_dict = NULL; Py_DECREF(d); return NULL; } PyObject* key = DK_ENTRIES(d->ma_keys)[pos].me_key; assert(key != NULL); di->di_pos++; di->len--; Py_INCREF(key); return key; } static PyTypeObject PyFrozenDictIterKey_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".keyiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)frozendictiter_iternextkey, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject* frozendictiter_iternextvalue(dictiterobject* di) { Py_ssize_t pos = di->di_pos; assert(pos >= 0); PyDictObject* d = di->di_dict; if (d == NULL) { return NULL; } assert(PyAnyFrozenDict_Check(d)); if (pos >= d->ma_used) { di->di_dict = NULL; Py_DECREF(d); return NULL; } PyObject* val = DK_ENTRIES(d->ma_keys)[pos].me_value; assert(val != NULL); di->di_pos++; di->len--; Py_INCREF(val); return val; } static PyTypeObject PyFrozenDictIterValue_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".valueiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)frozendictiter_iternextvalue, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject* frozendictiter_iternextitem(dictiterobject* di) { Py_ssize_t pos = di->di_pos; assert(pos >= 0); PyDictObject* d = di->di_dict; if (d == NULL) { return NULL; } assert(PyAnyFrozenDict_Check(d)); if (pos >= d->ma_used) { di->di_dict = NULL; Py_DECREF(d); return NULL; } PyDictKeyEntry* entry_ptr = &DK_ENTRIES(d->ma_keys)[pos]; PyObject* key = entry_ptr->me_key; PyObject* val = entry_ptr->me_value; assert(key != NULL); assert(val != NULL); di->di_pos++; di->len--; Py_INCREF(key); Py_INCREF(val); PyObject* result; if (Py_REFCNT(di->di_result) == 1) { result = di->di_result; PyObject *oldkey = PyTuple_GET_ITEM(result, 0); PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); if (!_PyObject_GC_IS_TRACKED(result)) { PyObject_GC_Track(result); } } else { result = PyTuple_New(2); if (result == NULL) return NULL; } PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, val); /* steals reference */ return result; } static PyTypeObject PyFrozenDictIterItem_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".itemiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)frozendictiter_iternextitem, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; /*** dict_keys ***/ static PyObject * frozendictkeys_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyFrozenDictIterKey_Type); } static PyTypeObject PyFrozenDictKeys_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".keys", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictkeys_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendictkeys_iter, /* tp_iter */ 0, /* tp_iternext */ dictkeys_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * frozendictkeys_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _d_PyDictView_New(dict, &PyFrozenDictKeys_Type); } /*** dict_items ***/ static PyObject * frozendictitems_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyFrozenDictIterItem_Type); } static PyTypeObject PyFrozenDictItems_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".items", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictitems_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendictitems_iter, /* tp_iter */ 0, /* tp_iternext */ dictitems_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * frozendictitems_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _d_PyDictView_New(dict, &PyFrozenDictItems_Type); } /*** dict_values ***/ static PyObject * frozendictvalues_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyFrozenDictIterValue_Type); } static PyTypeObject PyFrozenDictValues_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".values", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ 0, /* tp_as_number */ &dictvalues_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendictvalues_iter, /* tp_iter */ 0, /* tp_iternext */ dictvalues_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * frozendictvalues_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _d_PyDictView_New(dict, &PyFrozenDictValues_Type); } static int frozendict_exec(PyObject *m) { /* Finalize the type object including setting type of the new type * object; doing it here is required for portability, too. */ if (PyType_Ready(&PyFrozenDict_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictIterKey_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictIterValue_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictIterItem_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictKeys_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictItems_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictValues_Type) < 0) { goto fail; } PyModule_AddObject(m, FROZENDICT_CLASS_NAME, (PyObject *)&PyFrozenDict_Type); return 0; fail: Py_XDECREF(m); return -1; } static struct PyModuleDef_Slot frozendict_slots[] = { {Py_mod_exec, frozendict_exec}, {0, NULL}, }; static struct PyModuleDef frozendictmodule = { PyModuleDef_HEAD_INIT, FROZENDICT_MODULE_NAME, /* name of module */ NULL, /* module documentation, may be NULL */ 0, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ NULL, frozendict_slots, NULL, NULL, NULL }; PyMODINIT_FUNC PyInit__frozendict(void) { return PyModuleDef_Init(&frozendictmodule); } python-frozendict/src/frozendict/c_src/3_6/000077500000000000000000000000001470250572200211665ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_6/Include/000077500000000000000000000000001470250572200225515ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_6/Include/cpython/000077500000000000000000000000001470250572200242355ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_6/Include/cpython/frozendictobject.h000066400000000000000000000004601470250572200277440ustar00rootroot00000000000000#ifndef Py_CPYTHON_FROZENDICTOBJECT_H # error "this header file must not be included directly" #endif typedef struct { PyObject_HEAD Py_ssize_t ma_used; uint64_t ma_version_tag; PyDictKeysObject* ma_keys; PyObject** ma_values; Py_hash_t ma_hash; } PyFrozenDictObject; python-frozendict/src/frozendict/c_src/3_6/Include/frozendictobject.h000066400000000000000000000050501470250572200262600ustar00rootroot00000000000000#ifndef Py_FROZENDICTOBJECT_H #define Py_FROZENDICTOBJECT_H #ifdef __cplusplus extern "C" { #endif // PyAPI_DATA(PyTypeObject) PyFrozenDict_Type; static PyTypeObject PyFrozenDict_Type; // PyAPI_DATA(PyTypeObject) PyCoold_Type; static PyTypeObject PyCoold_Type; #define PyFrozenDict_Check(op) \ ( \ Py_IS_TYPE(op, &PyFrozenDict_Type) \ || PyType_IsSubtype(Py_TYPE(op), &PyFrozenDict_Type) \ ) #define PyFrozenDict_CheckExact(op) Py_IS_TYPE(op, &PyFrozenDict_Type) #define PyAnyFrozenDict_Check(op) \ ( \ Py_IS_TYPE(op, &PyFrozenDict_Type) \ || Py_IS_TYPE(op, &PyCoold_Type) \ || PyType_IsSubtype(Py_TYPE(op), &PyFrozenDict_Type) \ || PyType_IsSubtype(Py_TYPE(op), &PyCoold_Type) \ ) #define PyAnyFrozenDict_CheckExact(op) \ ( \ Py_IS_TYPE(op, &PyFrozenDict_Type) \ || Py_IS_TYPE(op, &PyCoold_Type) \ ) #define PyAnyDict_Check(ob) \ ( \ PyDict_Check(ob) \ || Py_IS_TYPE(ob, &PyFrozenDict_Type) \ || Py_IS_TYPE(ob, &PyCoold_Type) \ || PyType_IsSubtype(Py_TYPE(ob), &PyFrozenDict_Type) \ || PyType_IsSubtype(Py_TYPE(ob), &PyCoold_Type) \ ) #define PyAnyDict_CheckExact(op) ( \ (Py_IS_TYPE(op, &PyDict_Type)) \ || (Py_IS_TYPE(op, &PyFrozenDict_Type)) \ || (Py_IS_TYPE(op, &PyCoold_Type)) \ ) // PyAPI_DATA(PyTypeObject) PyFrozenDictIterKey_Type; static PyTypeObject PyFrozenDictIterKey_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictIterValues_Type; static PyTypeObject PyFrozenDictIterValue_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictIterItem_Type; static PyTypeObject PyFrozenDictIterItem_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictKeys_Type; static PyTypeObject PyFrozenDictKeys_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictValues_Type; static PyTypeObject PyFrozenDictValues_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictItems_Type; static PyTypeObject PyFrozenDictItems_Type; #define PyAnyDictKeys_Check(op) (PyDictKeys_Check(op) || PyObject_TypeCheck(op, &PyFrozenDictKeys_Type)) #define PyAnyDictValues_Check(op) (PyDictValues_Check(op) || PyObject_TypeCheck(op, &PyFrozenDictValues_Type)) #define PyAnyDictItems_Check(op) (PyDictItems_Check(op) || PyObject_TypeCheck(op, &PyFrozenDictItems_Type)) /* This excludes Values, since they are not sets. */ # define PyAnyDictViewSet_Check(op) \ (PyAnyDictKeys_Check(op) || PyAnyDictItems_Check(op)) #ifndef Py_LIMITED_API # define Py_CPYTHON_FROZENDICTOBJECT_H # include "cpython/frozendictobject.h" # undef Py_CPYTHON_FROZENDICTOBJECT_H #endif #ifdef __cplusplus } #endif #endif python-frozendict/src/frozendict/c_src/3_6/cpython_src/000077500000000000000000000000001470250572200235215ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_6/cpython_src/Objects/000077500000000000000000000000001470250572200251125ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_6/cpython_src/Objects/clinic/000077500000000000000000000000001470250572200263535ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_6/cpython_src/Objects/clinic/dictobject.c.h000066400000000000000000000026561470250572200310700ustar00rootroot00000000000000/*[clinic input] preserve [clinic start generated code]*/ PyDoc_STRVAR(dict_fromkeys__doc__, "fromkeys($type, iterable, value=None, /)\n" "--\n" "\n" "Create a new dictionary with keys from iterable and values set to value."); #define DICT_FROMKEYS_METHODDEF \ {"fromkeys", (PyCFunction)(void(*)(void))dict_fromkeys, METH_FASTCALL|METH_CLASS, dict_fromkeys__doc__}, PyDoc_STRVAR(dict___contains____doc__, "__contains__($self, key, /)\n" "--\n" "\n" "True if the dictionary has the specified key, else False."); #define DICT___CONTAINS___METHODDEF \ {"__contains__", (PyCFunction)dict___contains__, METH_O|METH_COEXIST, dict___contains____doc__}, PyDoc_STRVAR(dict_get__doc__, "get($self, key, default=None, /)\n" "--\n" "\n" "Return the value for key if key is in the dictionary, else default."); #define DICT_GET_METHODDEF \ {"get", (PyCFunction)dict_get, METH_VARARGS,dict_get__doc__}, PyDoc_STRVAR(dict___reversed____doc__, "__reversed__($self, /)\n" "--\n" "\n" "Return a reverse iterator over the dict keys."); #define DICT___REVERSED___METHODDEF \ {"__reversed__", (PyCFunction)dict___reversed__, METH_NOARGS, dict___reversed____doc__}, static PyObject * dict___reversed___impl(PyDictObject *self); static PyObject * dict___reversed__(PyDictObject *self, PyObject *Py_UNUSED(ignored)) { return dict___reversed___impl(self); } /*[clinic end generated code: output=7b77c16e43d6735a input=a9049054013a1b77]*/ python-frozendict/src/frozendict/c_src/3_6/cpython_src/Objects/dict-common.h000066400000000000000000000043611470250572200275000ustar00rootroot00000000000000#ifndef Py_DICT_COMMON_H #define Py_DICT_COMMON_H typedef struct { /* Cached hash code of me_key. */ Py_hash_t me_hash; PyObject *me_key; PyObject *me_value; /* This field is only meaningful for combined tables */ } PyDictKeyEntry; /* dict_lookup_func() returns index of entry which can be used like DK_ENTRIES(dk)[index]. * -1 when no entry found, -3 when compare raises error. */ typedef Py_ssize_t (*dict_lookup_func) (PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos); #define DKIX_EMPTY (-1) #define DKIX_DUMMY (-2) /* Used internally */ #define DKIX_ERROR (-3) /* See dictobject.c for actual layout of DictKeysObject */ struct _dictkeysobject { Py_ssize_t dk_refcnt; /* Size of the hash table (dk_indices). It must be a power of 2. */ Py_ssize_t dk_size; /* Function to lookup in the hash table (dk_indices): - lookdict(): general-purpose, and may return DKIX_ERROR if (and only if) a comparison raises an exception. - lookdict_unicode(): specialized to Unicode string keys, comparison of which can never raise an exception; that function can never return DKIX_ERROR. - lookdict_unicode_nodummy(): similar to lookdict_unicode() but further specialized for Unicode string keys that cannot be the value. - lookdict_split(): Version of lookdict() for split tables. */ dict_lookup_func dk_lookup; /* Number of usable entries in dk_entries. */ Py_ssize_t dk_usable; /* Number of used entries in dk_entries. */ Py_ssize_t dk_nentries; /* Actual hash table of dk_size entries. It holds indices in dk_entries, or DKIX_EMPTY(-1) or DKIX_DUMMY(-2). Indices must be: 0 <= indice < USABLE_FRACTION(dk_size). The size in bytes of an indice depends on dk_size: - 1 byte if dk_size <= 0xff (char*) - 2 bytes if dk_size <= 0xffff (int16_t*) - 4 bytes if dk_size <= 0xffffffff (int32_t*) - 8 bytes otherwise (int64_t*) Dynamically sized, SIZEOF_VOID_P is minimum. */ char dk_indices[]; /* char is required to avoid strict aliasing. */ /* "PyDictKeyEntry dk_entries[dk_usable];" array follows: see the DK_ENTRIES() macro */ }; #endif python-frozendict/src/frozendict/c_src/3_6/cpython_src/Objects/dictobject.c000066400000000000000000001730411470250572200273760ustar00rootroot00000000000000/* Dictionary object implementation using a hash table */ /* The distribution includes a separate file, Objects/dictnotes.txt, describing explorations into dictionary design and optimization. It covers typical dictionary use patterns, the parameters for tuning dictionaries, and several ideas for possible optimizations. */ /* PyDictKeysObject This implements the dictionary's hashtable. As of Python 3.6, this is compact and ordered. Basic idea is described here: * https://mail.python.org/pipermail/python-dev/2012-December/123028.html * https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html layout: +---------------+ | dk_refcnt | | dk_size | | dk_lookup | | dk_usable | | dk_nentries | +---------------+ | dk_indices | | | +---------------+ | dk_entries | | | +---------------+ dk_indices is actual hashtable. It holds index in entries, or DKIX_EMPTY(-1) or DKIX_DUMMY(-2). Size of indices is dk_size. Type of each index in indices is vary on dk_size: * int8 for dk_size <= 128 * int16 for 256 <= dk_size <= 2**15 * int32 for 2**16 <= dk_size <= 2**31 * int64 for 2**32 <= dk_size dk_entries is array of PyDictKeyEntry. Its size is USABLE_FRACTION(dk_size). DK_ENTRIES(dk) can be used to get pointer to entries. NOTE: Since negative value is used for DKIX_EMPTY and DKIX_DUMMY, type of dk_indices entry is signed integer and int16 is used for table which dk_size == 256. */ /* The DictObject can be in one of two forms. Either: A combined table: ma_values == NULL, dk_refcnt == 1. Values are stored in the me_value field of the PyDictKeysObject. Or: A split table: ma_values != NULL, dk_refcnt >= 1 Values are stored in the ma_values array. Only string (unicode) keys are allowed. All dicts sharing same key must have same insertion order. There are four kinds of slots in the table (slot is index, and DK_ENTRIES(keys)[index] if index >= 0): 1. Unused. index == DKIX_EMPTY Does not hold an active (key, value) pair now and never did. Unused can transition to Active upon key insertion. This is each slot's initial state. 2. Active. index >= 0, me_key != NULL and me_value != NULL Holds an active (key, value) pair. Active can transition to Dummy or Pending upon key deletion (for combined and split tables respectively). This is the only case in which me_value != NULL. 3. Dummy. index == DKIX_DUMMY (combined only) Previously held an active (key, value) pair, but that was deleted and an active pair has not yet overwritten the slot. Dummy can transition to Active upon key insertion. Dummy slots cannot be made Unused again else the probe sequence in case of collision would have no way to know they were once active. 4. Pending. index >= 0, key != NULL, and value == NULL (split only) Not yet inserted in split-table. */ /* Preserving insertion order It's simple for combined table. Since dk_entries is mostly append only, we can get insertion order by just iterating dk_entries. One exception is .popitem(). It removes last item in dk_entries and decrement dk_nentries to achieve amortized O(1). Since there are DKIX_DUMMY remains in dk_indices, we can't increment dk_usable even though dk_nentries is decremented. In split table, inserting into pending entry is allowed only for dk_entries[ix] where ix == mp->ma_used. Inserting into other index and deleting item cause converting the dict to the combined table. */ /* PyDict_MINSIZE is the starting size for any new dict. * 8 allows dicts with no more than 5 active entries; experiments suggested * this suffices for the majority of dicts (consisting mostly of usually-small * dicts created to pass keyword arguments). * Making this 8, rather than 4 reduces the number of resizes for most * dictionaries, without any significant extra memory use. */ #define PyDict_MINSIZE 8 #include "Python.h" #include "dict-common.h" #include "stringlib/eq.h" // unicode_eq() /*[clinic input] class dict "PyDictObject *" "&PyDict_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=f157a5a0ce9589d6]*/ /* To ensure the lookup algorithm terminates, there must be at least one Unused slot (NULL key) in the table. To avoid slowing down lookups on a near-full table, we resize the table when it's USABLE_FRACTION (currently two-thirds) full. */ #define PERTURB_SHIFT 5 /* Major subtleties ahead: Most hash schemes depend on having a "good" hash function, in the sense of simulating randomness. Python doesn't: its most important hash functions (for ints) are very regular in common cases: >>>[hash(i) for i in range(4)] [0, 1, 2, 3] This isn't necessarily bad! To the contrary, in a table of size 2**i, taking the low-order i bits as the initial table index is extremely fast, and there are no collisions at all for dicts indexed by a contiguous range of ints. So this gives better-than-random behavior in common cases, and that's very desirable. OTOH, when collisions occur, the tendency to fill contiguous slices of the hash table makes a good collision resolution strategy crucial. Taking only the last i bits of the hash code is also vulnerable: for example, consider the list [i << 16 for i in range(20000)] as a set of keys. Since ints are their own hash codes, and this fits in a dict of size 2**15, the last 15 bits of every hash code are all 0: they *all* map to the same table index. But catering to unusual cases should not slow the usual ones, so we just take the last i bits anyway. It's up to collision resolution to do the rest. If we *usually* find the key we're looking for on the first try (and, it turns out, we usually do -- the table load factor is kept under 2/3, so the odds are solidly in our favor), then it makes best sense to keep the initial index computation dirt cheap. The first half of collision resolution is to visit table indices via this recurrence: j = ((5*j) + 1) mod 2**i For any initial j in range(2**i), repeating that 2**i times generates each int in range(2**i) exactly once (see any text on random-number generation for proof). By itself, this doesn't help much: like linear probing (setting j += 1, or j -= 1, on each loop trip), it scans the table entries in a fixed order. This would be bad, except that's not the only thing we do, and it's actually *good* in the common cases where hash keys are consecutive. In an example that's really too small to make this entirely clear, for a table of size 2**3 the order of indices is: 0 -> 1 -> 6 -> 7 -> 4 -> 5 -> 2 -> 3 -> 0 [and here it's repeating] If two things come in at index 5, the first place we look after is index 2, not 6, so if another comes in at index 6 the collision at 5 didn't hurt it. Linear probing is deadly in this case because there the fixed probe order is the *same* as the order consecutive keys are likely to arrive. But it's extremely unlikely hash codes will follow a 5*j+1 recurrence by accident, and certain that consecutive hash codes do not. The other half of the strategy is to get the other bits of the hash code into play. This is done by initializing a (unsigned) vrbl "perturb" to the full hash code, and changing the recurrence to: perturb >>= PERTURB_SHIFT; j = (5*j) + 1 + perturb; use j % 2**i as the next table index; Now the probe sequence depends (eventually) on every bit in the hash code, and the pseudo-scrambling property of recurring on 5*j+1 is more valuable, because it quickly magnifies small differences in the bits that didn't affect the initial index. Note that because perturb is unsigned, if the recurrence is executed often enough perturb eventually becomes and remains 0. At that point (very rarely reached) the recurrence is on (just) 5*j+1 again, and that's certain to find an empty slot eventually (since it generates every int in range(2**i), and we make sure there's always at least one empty slot). Selecting a good value for PERTURB_SHIFT is a balancing act. You want it small so that the high bits of the hash code continue to affect the probe sequence across iterations; but you want it large so that in really bad cases the high-order hash bits have an effect on early iterations. 5 was "the best" in minimizing total collisions across experiments Tim Peters ran (on both normal and pathological cases), but 4 and 6 weren't significantly worse. Historical: Reimer Behrends contributed the idea of using a polynomial-based approach, using repeated multiplication by x in GF(2**n) where an irreducible polynomial for each table size was chosen such that x was a primitive root. Christian Tismer later extended that to use division by x instead, as an efficient way to get the high bits of the hash code into play. This scheme also gave excellent collision statistics, but was more expensive: two if-tests were required inside the loop; computing "the next" index took about the same number of operations but without as much potential parallelism (e.g., computing 5*j can go on at the same time as computing 1+perturb in the above, and then shifting perturb can be done while the table index is being masked); and the PyDictObject struct required a member to hold the table's polynomial. In Tim's experiments the current scheme ran faster, produced equally good collision statistics, needed less code & used less memory. */ /* forward declarations */ static Py_ssize_t lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos); static Py_ssize_t lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos); /*Global counter used to set ma_version_tag field of dictionary. * It is incremented each time that a dictionary is created and each * time that a dictionary is modified. */ static uint64_t pydict_global_version = 0; #define DICT_NEXT_VERSION() (++pydict_global_version) /* Dictionary reuse scheme to save calls to malloc and free */ #ifndef PyDict_MAXFREELIST #define PyDict_MAXFREELIST 80 #endif #if PyDict_MAXFREELIST > 0 static PyDictObject *free_list[PyDict_MAXFREELIST]; static int numfree = 0; static PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST]; static int numfreekeys = 0; #endif #include "clinic/dictobject.c.h" #define DK_SIZE(dk) ((dk)->dk_size) #if SIZEOF_VOID_P > 4 #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : DK_SIZE(dk) <= 0xffffffff ? \ 4 : sizeof(int64_t)) #else #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : sizeof(int32_t)) #endif #define DK_ENTRIES(dk) \ ((PyDictKeyEntry*)(&((int8_t*)((dk)->dk_indices))[DK_SIZE(dk) * DK_IXSIZE(dk)])) #define DK_MASK(dk) (((dk)->dk_size)-1) #define IS_POWER_OF_2(x) (((x) & (x-1)) == 0) static void free_keys_object(PyDictKeysObject *keys); static inline void dictkeys_incref(PyDictKeysObject *dk) { _Py_INC_REFTOTAL; dk->dk_refcnt++; } static inline void dictkeys_decref(PyDictKeysObject *dk) { assert(dk->dk_refcnt > 0); _Py_DEC_REFTOTAL; if (--dk->dk_refcnt == 0) { free_keys_object(dk); } } /* lookup indices. returns DKIX_EMPTY, DKIX_DUMMY, or ix >=0 */ static inline Py_ssize_t dictkeys_get_index(const PyDictKeysObject *keys, Py_ssize_t i) { Py_ssize_t s = DK_SIZE(keys); Py_ssize_t ix; if (s <= 0xff) { const int8_t *indices = (const int8_t*)(keys->dk_indices); ix = indices[i]; } else if (s <= 0xffff) { const int16_t *indices = (const int16_t*)(keys->dk_indices); ix = indices[i]; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { const int64_t *indices = (const int64_t*)(keys->dk_indices); ix = indices[i]; } #endif else { const int32_t *indices = (const int32_t*)(keys->dk_indices); ix = indices[i]; } assert(ix >= DKIX_DUMMY); return ix; } /* write to indices. */ static inline void dictkeys_set_index(PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix) { Py_ssize_t s = DK_SIZE(keys); assert(ix >= DKIX_DUMMY); if (s <= 0xff) { int8_t *indices = (int8_t*)(keys->dk_indices); assert(ix <= 0x7f); indices[i] = (char)ix; } else if (s <= 0xffff) { int16_t *indices = (int16_t*)(keys->dk_indices); assert(ix <= 0x7fff); indices[i] = (int16_t)ix; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { int64_t *indices = (int64_t*)(keys->dk_indices); indices[i] = ix; } #endif else { int32_t *indices = (int32_t*)(keys->dk_indices); assert(ix <= 0x7fffffff); indices[i] = (int32_t)ix; } } /* USABLE_FRACTION is the maximum dictionary load. * Increasing this ratio makes dictionaries more dense resulting in more * collisions. Decreasing it improves sparseness at the expense of spreading * indices over more cache lines and at the cost of total memory consumed. * * USABLE_FRACTION must obey the following: * (0 < USABLE_FRACTION(n) < n) for all n >= 2 * * USABLE_FRACTION should be quick to calculate. * Fractions around 1/2 to 2/3 seem to work well in practice. */ #define USABLE_FRACTION(n) (((n) << 1)/3) /* Find the smallest dk_size >= minsize. */ static inline Py_ssize_t calculate_keysize(Py_ssize_t minsize) { #if SIZEOF_LONG == SIZEOF_SIZE_T minsize = (minsize | PyDict_MINSIZE) - 1; return 1LL << _Py_bit_length(minsize | (PyDict_MINSIZE-1)); #elif defined(_MSC_VER) // On 64bit Windows, sizeof(long) == 4. minsize = (minsize | PyDict_MINSIZE) - 1; unsigned long msb; _BitScanReverse64(&msb, (uint64_t)minsize); return 1LL << (msb + 1); #else Py_ssize_t size; for (size = PyDict_MINSIZE; size < minsize && size > 0; size <<= 1) ; return size; #endif } /* estimate_keysize is reverse function of USABLE_FRACTION. * * This can be used to reserve enough size to insert n entries without * resizing. */ static inline Py_ssize_t estimate_keysize(Py_ssize_t n) { return calculate_keysize((n*3 + 1) / 2); } /* GROWTH_RATE. Growth rate upon hitting maximum load. * Currently set to used*3. * This means that dicts double in size when growing without deletions, * but have more head room when the number of deletions is on a par with the * number of insertions. See also bpo-17563 and bpo-33205. * * GROWTH_RATE was set to used*4 up to version 3.2. * GROWTH_RATE was set to used*2 in version 3.3.0 * GROWTH_RATE was set to used*2 + capacity/2 in 3.4.0-3.6.0. */ #define GROWTH_RATE(d) ((d)->ma_used*3) /* This immutable, empty PyDictKeysObject is used for PyDict_Clear() * (which cannot fail and thus can do no allocation). */ static PyDictKeysObject empty_keys_struct = { 1, /* dk_refcnt */ 1, /* dk_size */ lookdict_unicode_nodummy, /* dk_lookup */ 0, /* dk_usable (immutable) */ 0, /* dk_nentries */ {DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY}, /* dk_indices */ }; static PyObject *empty_values[1] = { NULL }; #define Py_EMPTY_KEYS &empty_keys_struct /* Uncomment to check the dict content in _PyDict_CheckConsistency() */ /* #define DEBUG_PYDICT */ #ifdef DEBUG_PYDICT # define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 1)) #else # define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 0)) #endif #ifndef NDEBUG static int _PyDict_CheckConsistency(PyObject *op, int check_content) { assert(op != NULL); assert(PyAnyDict_Check(op)); PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; int splitted = _PyDict_HasSplitTable(mp); Py_ssize_t usable = USABLE_FRACTION(keys->dk_size); assert(0 <= mp->ma_used && mp->ma_used <= usable); assert(IS_POWER_OF_2(keys->dk_size)); assert(0 <= keys->dk_usable && keys->dk_usable <= usable); assert(0 <= keys->dk_nentries && keys->dk_nentries <= usable); assert(keys->dk_usable + keys->dk_nentries <= usable); if (!splitted) { /* combined table */ assert(keys->dk_refcnt == 1); } if (check_content) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i; for (i=0; i < keys->dk_size; i++) { Py_ssize_t ix = dictkeys_get_index(keys, i); assert(DKIX_DUMMY <= ix && ix <= usable); } for (i=0; i < usable; i++) { PyDictKeyEntry *entry = &entries[i]; PyObject *key = entry->me_key; if (key != NULL) { if (PyUnicode_CheckExact(key)) { Py_hash_t hash = ((PyASCIIObject *)key)->hash; assert(hash != -1); assert(entry->me_hash == hash); } else { /* test_dict fails if PyObject_Hash() is called again */ assert(entry->me_hash != -1); } if (!splitted) { assert(entry->me_value != NULL); } } if (splitted) { assert(entry->me_value == NULL); } } if (splitted) { /* splitted table */ for (i=0; i < mp->ma_used; i++) { assert(mp->ma_values[i] != NULL); } } } return 1; } #endif static PyDictKeysObject* new_keys_object(Py_ssize_t size) { PyDictKeysObject *dk; Py_ssize_t es, usable; assert(size >= PyDict_MINSIZE); assert(IS_POWER_OF_2(size)); usable = USABLE_FRACTION(size); if (size <= 0xff) { es = 1; } else if (size <= 0xffff) { es = 2; } #if SIZEOF_VOID_P > 4 else if (size <= 0xffffffff) { es = 4; } #endif else { es = sizeof(Py_ssize_t); } #if PyDict_MAXFREELIST > 0 if (size == PyDict_MINSIZE && numfreekeys > 0) { dk = keys_free_list[--numfreekeys]; } else #endif { dk = PyObject_Malloc(sizeof(PyDictKeysObject) + es * size + sizeof(PyDictKeyEntry) * usable); if (dk == NULL) { PyErr_NoMemory(); return NULL; } } _Py_INC_REFTOTAL; dk->dk_refcnt = 1; dk->dk_size = size; dk->dk_usable = usable; dk->dk_lookup = lookdict_unicode_nodummy; dk->dk_nentries = 0; memset(&dk->dk_indices[0], 0xff, es * size); memset(DK_ENTRIES(dk), 0, sizeof(PyDictKeyEntry) * usable); return dk; } static void free_keys_object(PyDictKeysObject *keys) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n; for (i = 0, n = keys->dk_nentries; i < n; i++) { Py_XDECREF(entries[i].me_key); Py_XDECREF(entries[i].me_value); } #if PyDict_MAXFREELIST > 0 if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) { keys_free_list[numfreekeys++] = keys; return; } #endif PyObject_Free(keys); } #define new_values(size) PyMem_NEW(PyObject *, size) #define free_values(values) PyMem_Free(values) static Py_ssize_t _d_PyDict_KeysSize(PyDictKeysObject *keys) { return (sizeof(PyDictKeysObject) + DK_IXSIZE(keys) * DK_SIZE(keys) + USABLE_FRACTION(DK_SIZE(keys)) * sizeof(PyDictKeyEntry)); } static PyDictKeysObject * clone_combined_dict_keys(PyDictObject *orig) { assert(PyAnyDict_Check(orig)); assert( Py_TYPE(orig)->tp_iter == PyDict_Type.tp_iter || Py_TYPE(orig)->tp_iter == (getiterfunc)frozendict_iter ); assert(orig->ma_values == NULL); assert(orig->ma_keys->dk_refcnt == 1); Py_ssize_t keys_size = _d_PyDict_KeysSize(orig->ma_keys); PyDictKeysObject *keys = PyObject_Malloc(keys_size); if (keys == NULL) { PyErr_NoMemory(); return NULL; } memcpy(keys, orig->ma_keys, keys_size); /* After copying key/value pairs, we need to incref all keys and values and they are about to be co-owned by a new dict object. */ PyDictKeyEntry *ep0 = DK_ENTRIES(keys); Py_ssize_t n = keys->dk_nentries; for (Py_ssize_t i = 0; i < n; i++) { PyDictKeyEntry *entry = &ep0[i]; PyObject *value = entry->me_value; Py_INCREF(value); Py_INCREF(entry->me_key); } /* Since we copied the keys table we now have an extra reference in the system. Manually call _Py_INC_REFTOTAL to signal that we have it now; calling dictkeys_incref would be an error as keys->dk_refcnt is already set to 1 (after memcpy). */ _Py_INC_REFTOTAL; return keys; } /* The basic lookup function used by all operations. This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4. Open addressing is preferred over chaining since the link overhead for chaining would be substantial (100% with typical malloc overhead). The initial probe index is computed as hash mod the table size. Subsequent probe indices are computed as explained earlier. All arithmetic on hash should ignore overflow. The details in this version are due to Tim Peters, building on many past contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and Christian Tismer. lookdict() is general-purpose, and may return DKIX_ERROR if (and only if) a comparison raises an exception. lookdict_unicode() below is specialized to string keys, comparison of which can never raise an exception; that function can never return DKIX_ERROR when key is string. Otherwise, it falls back to lookdict(). lookdict_unicode_nodummy is further specialized for string keys that cannot be the value. For both, when the key isn't found a DKIX_EMPTY is returned. hashpos returns where the key index should be inserted. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos) { size_t i, mask, perturb; PyDictKeysObject *dk; PyDictKeyEntry *ep0; top: dk = mp->ma_keys; ep0 = DK_ENTRIES(dk); mask = DK_MASK(dk); perturb = hash; i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(dk, i); if (ix == DKIX_EMPTY) { if (hashpos != NULL) *hashpos = i; *value_addr = NULL; return ix; } if (ix >= 0) { PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); if (ep->me_key == key) { *value_addr = &ep->me_value; if (hashpos != NULL) *hashpos = i; return ix; } if (ep->me_hash == hash) { PyObject *startkey = ep->me_key; Py_INCREF(startkey); int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); Py_DECREF(startkey); if (cmp < 0) { *value_addr = NULL; return DKIX_ERROR; } if (dk == mp->ma_keys && ep->me_key == startkey) { if (cmp > 0) { *value_addr = &ep->me_value; if (hashpos != NULL) *hashpos = i; return ix; } } else { /* The dict was mutated, restart */ goto top; } } } perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; } Py_UNREACHABLE(); } /* Faster version of lookdict_unicode when it is known that no keys * will be present. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos) { assert(mp->ma_values == NULL); /* Make sure this function doesn't have to handle non-unicode keys, including subclasses of str; e.g., one reason to subclass unicodes is to override __eq__, and for speed we don't cater to that here. */ if (!PyUnicode_CheckExact(key)) { return lookdict(mp, key, hash, value_addr, hashpos); } PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); size_t mask = DK_MASK(mp->ma_keys); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i); assert (ix != DKIX_DUMMY); if (ix == DKIX_EMPTY) { if (hashpos != NULL) *hashpos = i; *value_addr = NULL; return DKIX_EMPTY; } PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { if (hashpos != NULL) *hashpos = i; *value_addr = &ep->me_value; return ix; } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } #define MAINTAIN_TRACKING(mp, key, value) \ do { \ if (!_PyObject_GC_IS_TRACKED(mp)) { \ if (_PyObject_GC_MAY_BE_TRACKED(key) || \ _PyObject_GC_MAY_BE_TRACKED(value)) { \ PyObject_GC_Track(mp); \ } \ } \ } while(0) /* Internal function to find slot for an item from its hash when it is known that the key is not present in the dict. The dict must be combined. */ static Py_ssize_t find_empty_slot(PyDictKeysObject *keys, Py_hash_t hash) { assert(keys != NULL); const size_t mask = DK_MASK(keys); size_t i = hash & mask; Py_ssize_t ix = dictkeys_get_index(keys, i); for (size_t perturb = hash; ix >= 0;) { perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; ix = dictkeys_get_index(keys, i); } return i; } /* Internal routine used by dictresize() to build a hashtable of entries. */ static void build_indices(PyDictKeysObject *keys, PyDictKeyEntry *ep, Py_ssize_t n) { size_t mask = (size_t)DK_SIZE(keys) - 1; for (Py_ssize_t ix = 0; ix != n; ix++, ep++) { Py_hash_t hash = ep->me_hash; size_t i = hash & mask; for (size_t perturb = hash; dictkeys_get_index(keys, i) != DKIX_EMPTY;) { perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } dictkeys_set_index(keys, i, ix); } } static int _d_PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue, Py_hash_t *phash) { Py_ssize_t i; PyDictObject *mp; PyDictKeyEntry *entry_ptr; PyObject *value; if (!PyAnyDict_Check(op)) return 0; mp = (PyDictObject *)op; i = *ppos; if (mp->ma_values) { if (i < 0 || i >= mp->ma_used) return 0; /* values of split table is always dense */ entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; value = mp->ma_values[i]; assert(value != NULL); } else { Py_ssize_t n = mp->ma_keys->dk_nentries; if (i < 0 || i >= n) return 0; entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) return 0; value = entry_ptr->me_value; } *ppos = i+1; if (pkey) *pkey = entry_ptr->me_key; if (phash) *phash = entry_ptr->me_hash; if (pvalue) *pvalue = value; return 1; } /* Methods */ static void dict_dealloc(PyDictObject *mp) { PyObject **values = mp->ma_values; PyDictKeysObject *keys = mp->ma_keys; Py_ssize_t i, n; /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(mp); Py_TRASHCAN_SAFE_BEGIN(mp) if (values != NULL) { if (values != empty_values) { for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) { Py_XDECREF(values[i]); } free_values(values); } dictkeys_decref(keys); } else if (keys != NULL) { assert(keys->dk_refcnt == 1 || keys == Py_EMPTY_KEYS); dictkeys_decref(keys); } #if PyDict_MAXFREELIST > 0 if (numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type)) { free_list[numfree++] = mp; } else #endif { Py_TYPE(mp)->tp_free((PyObject *)mp); } Py_TRASHCAN_SAFE_END(mp) } static PyObject * dict_repr(PyDictObject *mp) { Py_ssize_t i; PyObject *key = NULL, *value = NULL; _PyUnicodeWriter writer; int first; i = Py_ReprEnter((PyObject *)mp); if (i != 0) { return i > 0 ? PyUnicode_FromString("{...}") : NULL; } if (mp->ma_used == 0) { Py_ReprLeave((PyObject *)mp); return PyUnicode_FromString("{}"); } _PyUnicodeWriter_Init(&writer); writer.overallocate = 1; /* "{" + "1: 2" + ", 3: 4" * (len - 1) + "}" */ writer.min_length = 1 + 4 + (2 + 4) * (mp->ma_used - 1) + 1; if (_PyUnicodeWriter_WriteChar(&writer, '{') < 0) goto error; /* Do repr() on each key+value pair, and insert ": " between them. Note that repr may mutate the dict. */ i = 0; first = 1; while (_d_PyDict_Next((PyObject *)mp, &i, &key, &value, NULL)) { PyObject *s; int res; /* Prevent repr from deleting key or value during key format. */ Py_INCREF(key); Py_INCREF(value); if (!first) { if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0) goto error; } first = 0; s = PyObject_Repr(key); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; if (_PyUnicodeWriter_WriteASCIIString(&writer, ": ", 2) < 0) goto error; s = PyObject_Repr(value); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; Py_CLEAR(key); Py_CLEAR(value); } writer.overallocate = 0; if (_PyUnicodeWriter_WriteChar(&writer, '}') < 0) goto error; Py_ReprLeave((PyObject *)mp); return _PyUnicodeWriter_Finish(&writer); error: Py_ReprLeave((PyObject *)mp); _PyUnicodeWriter_Dealloc(&writer); Py_XDECREF(key); Py_XDECREF(value); return NULL; } static Py_ssize_t dict_length(PyDictObject *mp) { return mp->ma_used; } static PyObject * dict_subscript(PyDictObject *mp, PyObject *key) { PyObject *v; Py_ssize_t ix; Py_hash_t hash; PyObject **value_addr; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || *value_addr == NULL) { if (!PyAnyDict_CheckExact(mp)) { /* Look up __missing__ method if we're a subclass. */ PyObject *missing, *res; _Py_IDENTIFIER(__missing__); missing = _PyObject_LookupSpecial((PyObject *)mp, &PyId___missing__); if (missing != NULL) { res = PyObject_CallFunctionObjArgs(missing, key, NULL); Py_DECREF(missing); return res; } else if (PyErr_Occurred()) return NULL; } _PyErr_SetKeyError(key); return NULL; } v = *value_addr; Py_INCREF(v); return v; } static PyObject * dict_richcompare(PyObject *v, PyObject *w, int op) { int cmp; PyObject *res; if (!PyAnyDict_Check(v) || !PyAnyDict_Check(w)) { res = Py_NotImplemented; } else if (op == Py_EQ || op == Py_NE) { cmp = frozendict_equal((PyDictObject *)v, (PyDictObject *)w); if (cmp < 0) return NULL; res = (cmp == (op == Py_EQ)) ? Py_True : Py_False; } else res = Py_NotImplemented; Py_INCREF(res); return res; } /*[clinic input] @coexist dict.__contains__ key: object / True if the dictionary has the specified key, else False. [clinic start generated code]*/ static PyObject * dict___contains__(PyDictObject *self, PyObject *key) /*[clinic end generated code: output=a3d03db709ed6e6b input=fe1cb42ad831e820]*/ { register PyDictObject *mp = self; Py_hash_t hash; Py_ssize_t ix; PyObject **value_addr; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || *value_addr == NULL) Py_RETURN_FALSE; Py_RETURN_TRUE; } static PyObject * dict_get(PyDictObject *self, PyObject *args) { PyObject *key; PyObject *default_value = Py_None; PyObject *val = NULL; Py_hash_t hash; Py_ssize_t ix; PyObject **value_addr; if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &default_value)) return NULL; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (self->ma_keys->dk_lookup) (self, key, hash, &value_addr, NULL); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || *value_addr == NULL) { val = default_value; } else { val = *value_addr; } Py_INCREF(val); return val; } static int dict_traverse(PyObject *op, visitproc visit, void *arg) { PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n = keys->dk_nentries; if (keys->dk_lookup == lookdict) { for (i = 0; i < n; i++) { if (entries[i].me_value != NULL) { Py_VISIT(entries[i].me_value); Py_VISIT(entries[i].me_key); } } } else { if (mp->ma_values != NULL) { for (i = 0; i < n; i++) { Py_VISIT(mp->ma_values[i]); } } else { for (i = 0; i < n; i++) { Py_VISIT(entries[i].me_value); } } } return 0; } static PyObject *dictiter_new(PyDictObject *, PyTypeObject *); static Py_ssize_t _d_PyDict_SizeOf(PyDictObject *mp) { Py_ssize_t size, usable, res; size = DK_SIZE(mp->ma_keys); usable = USABLE_FRACTION(size); res = _PyObject_SIZE(Py_TYPE(mp)); if (mp->ma_values) res += usable * sizeof(PyObject*); /* If the dictionary is split, the keys portion is accounted-for in the type object. */ if (mp->ma_keys->dk_refcnt == 1) res += (sizeof(PyDictKeysObject) + DK_IXSIZE(mp->ma_keys) * size + sizeof(PyDictKeyEntry) * usable); return res; } static PyObject * dict_sizeof(PyDictObject *mp, PyObject *Py_UNUSED(ignored)) { return PyLong_FromSsize_t(_d_PyDict_SizeOf(mp)); } PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]"); PyDoc_STRVAR(sizeof__doc__, "D.__sizeof__() -> size of D in memory, in bytes"); PyDoc_STRVAR(copy__doc__, "D.copy() -> a shallow copy of D"); PyDoc_STRVAR(keys__doc__, "D.keys() -> a set-like object providing a view on D's keys"); PyDoc_STRVAR(items__doc__, "D.items() -> a set-like object providing a view on D's items"); PyDoc_STRVAR(values__doc__, "D.values() -> an object providing a view on D's values"); /* Hack to implement "key in dict" */ static PySequenceMethods dict_as_sequence = { 0, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ PyDict_Contains, /* sq_contains */ 0, /* sq_inplace_concat */ 0, /* sq_inplace_repeat */ }; /* Dictionary iterator types */ typedef struct { PyObject_HEAD PyDictObject *di_dict; /* Set to NULL when iterator is exhausted */ Py_ssize_t di_used; Py_ssize_t di_pos; PyObject* di_result; /* reusable result tuple for iteritems */ Py_ssize_t len; } dictiterobject; PyTypeObject PyDictRevIterKey_Type; PyTypeObject PyDictRevIterValue_Type; PyTypeObject PyDictRevIterItem_Type; static PyObject * dictiter_new(PyDictObject *dict, PyTypeObject *itertype) { dictiterobject *di; di = PyObject_GC_New(dictiterobject, itertype); if (di == NULL) { return NULL; } Py_INCREF(dict); di->di_dict = dict; di->di_used = dict->ma_used; di->len = dict->ma_used; if (itertype == &PyDictRevIterKey_Type || itertype == &PyDictRevIterItem_Type || itertype == &PyDictRevIterValue_Type) { if (dict->ma_values) { di->di_pos = dict->ma_used - 1; } else { di->di_pos = dict->ma_keys->dk_nentries - 1; } } else { di->di_pos = 0; } if ( itertype == &PyFrozenDictIterItem_Type || itertype == &PyDictRevIterItem_Type ) { di->di_result = PyTuple_Pack(2, Py_None, Py_None); if (di->di_result == NULL) { Py_DECREF(di); return NULL; } } else { di->di_result = NULL; } PyObject_GC_Track(di); return (PyObject *)di; } static void dictiter_dealloc(dictiterobject *di) { /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(di); Py_XDECREF(di->di_dict); Py_XDECREF(di->di_result); PyObject_GC_Del(di); } static int dictiter_traverse(dictiterobject *di, visitproc visit, void *arg) { Py_VISIT(di->di_dict); Py_VISIT(di->di_result); return 0; } static PyObject * dictiter_len(dictiterobject *di, PyObject *Py_UNUSED(ignored)) { Py_ssize_t len = 0; if (di->di_dict != NULL && di->di_used == di->di_dict->ma_used) len = di->len; return PyLong_FromSize_t(len); } PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); static PyObject * dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyMethodDef dictiter_methods[] = { {"__length_hint__", (PyCFunction)(void(*)(void))dictiter_len, METH_NOARGS, length_hint_doc}, {"__reduce__", (PyCFunction)(void(*)(void))dictiter_reduce, METH_NOARGS, reduce_doc}, {NULL, NULL} /* sentinel */ }; /* dictreviter */ static PyObject * dictreviter_iternext(dictiterobject *di) { PyDictObject *d = di->di_dict; if (d == NULL) { return NULL; } assert (PyAnyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } Py_ssize_t i = di->di_pos; PyDictKeysObject *k = d->ma_keys; PyObject *key, *value, *result; if (i < 0) { goto fail; } if (d->ma_values) { key = DK_ENTRIES(k)[i].me_key; value = d->ma_values[i]; assert (value != NULL); } else { PyDictKeyEntry *entry_ptr = &DK_ENTRIES(k)[i]; while (entry_ptr->me_value == NULL) { if (--i < 0) { goto fail; } entry_ptr--; } key = entry_ptr->me_key; value = entry_ptr->me_value; } di->di_pos = i-1; di->len--; if (Py_IS_TYPE(di, &PyDictRevIterKey_Type)) { Py_INCREF(key); return key; } else if (Py_IS_TYPE(di, &PyDictRevIterValue_Type)) { Py_INCREF(value); return value; } else if (Py_IS_TYPE(di, &PyDictRevIterItem_Type)) { Py_INCREF(key); Py_INCREF(value); result = di->di_result; if (Py_REFCNT(result) == 1) { PyObject *oldkey = PyTuple_GET_ITEM(result, 0); PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); // bpo-42536: The GC may have untracked this result tuple. Since // we're recycling it, make sure it's tracked again: // if (!_PyObject_GC_IS_TRACKED(result)) { // _PyObject_GC_TRACK(result); // } } else { result = PyTuple_New(2); if (result == NULL) { return NULL; } PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ } return result; } else { Py_UNREACHABLE(); } fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictRevIterKey_Type = { PyVarObject_HEAD_INIT(NULL, 0) "dict_reversekeyiterator", sizeof(dictiterobject), .tp_dealloc = (destructor)dictiter_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_traverse = (traverseproc)dictiter_traverse, .tp_iter = PyObject_SelfIter, .tp_iternext = (iternextfunc)dictreviter_iternext, .tp_methods = dictiter_methods }; PyTypeObject PyDictRevIterItem_Type = { PyVarObject_HEAD_INIT(NULL, 0) "dict_reverseitemiterator", sizeof(dictiterobject), .tp_dealloc = (destructor)dictiter_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_traverse = (traverseproc)dictiter_traverse, .tp_iter = PyObject_SelfIter, .tp_iternext = (iternextfunc)dictreviter_iternext, .tp_methods = dictiter_methods }; PyTypeObject PyDictRevIterValue_Type = { PyVarObject_HEAD_INIT(NULL, 0) "dict_reversevalueiterator", sizeof(dictiterobject), .tp_dealloc = (destructor)dictiter_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_traverse = (traverseproc)dictiter_traverse, .tp_iter = PyObject_SelfIter, .tp_iternext = (iternextfunc)dictreviter_iternext, .tp_methods = dictiter_methods }; /*[clinic input] dict.__reversed__ Return a reverse iterator over the dict keys. [clinic start generated code]*/ static PyObject * dict___reversed___impl(PyDictObject *self) /*[clinic end generated code: output=e674483336d1ed51 input=23210ef3477d8c4d]*/ { assert (PyAnyDict_Check(self)); return dictiter_new(self, &PyDictRevIterKey_Type); } static PyObject * dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)) { _Py_IDENTIFIER(iter); /* copy the iterator state */ dictiterobject tmp = *di; Py_XINCREF(tmp.di_dict); PyObject *list = PySequence_List((PyObject*)&tmp); Py_XDECREF(tmp.di_dict); if (list == NULL) { return NULL; } return Py_BuildValue("N(N)", _PyEval_GetBuiltinId(&PyId_iter), list); } /***********************************************/ /* View objects for keys(), items(), values(). */ /***********************************************/ /* The instance lay-out is the same for all three; but the type differs. */ static void dictview_dealloc(_PyDictViewObject *dv) { /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(dv); Py_XDECREF(dv->dv_dict); PyObject_GC_Del(dv); } static int dictview_traverse(_PyDictViewObject *dv, visitproc visit, void *arg) { Py_VISIT(dv->dv_dict); return 0; } static Py_ssize_t dictview_len(_PyDictViewObject *dv) { Py_ssize_t len = 0; if (dv->dv_dict != NULL) len = dv->dv_dict->ma_used; return len; } static PyObject * _d_PyDictView_New(PyObject *dict, PyTypeObject *type) { _PyDictViewObject *dv; if (dict == NULL) { PyErr_BadInternalCall(); return NULL; } if (!PyAnyDict_Check(dict)) { /* XXX Get rid of this restriction later */ PyErr_Format(PyExc_TypeError, "%s() requires a dict argument, not '%s'", type->tp_name, Py_TYPE(dict)->tp_name); return NULL; } dv = PyObject_GC_New(_PyDictViewObject, type); if (dv == NULL) return NULL; Py_INCREF(dict); dv->dv_dict = (PyDictObject *)dict; PyObject_GC_Track(dv); return (PyObject *)dv; } static PyObject * dictview_mapping(PyObject *view, void *Py_UNUSED(ignored)) { assert(view != NULL); assert(PyAnyDictKeys_Check(view) || PyAnyDictValues_Check(view) || PyAnyDictItems_Check(view)); PyObject *mapping = (PyObject *)((_PyDictViewObject *)view)->dv_dict; return PyDictProxy_New(mapping); } static PyGetSetDef dictview_getset[] = { {"mapping", dictview_mapping, (setter)NULL, "dictionary that this view refers to", NULL}, {0} }; /* TODO(guido): The views objects are not complete: * support more set operations * support arbitrary mappings? - either these should be static or exported in dictobject.h - if public then they should probably be in builtins */ /* Return 1 if self is a subset of other, iterating over self; 0 if not; -1 if an error occurred. */ static int all_contained_in(PyObject *self, PyObject *other) { PyObject *iter = PyObject_GetIter(self); int ok = 1; if (iter == NULL) return -1; for (;;) { PyObject *next = PyIter_Next(iter); if (next == NULL) { if (PyErr_Occurred()) ok = -1; break; } ok = PySequence_Contains(other, next); Py_DECREF(next); if (ok <= 0) break; } Py_DECREF(iter); return ok; } static PyObject * dictview_richcompare(PyObject *self, PyObject *other, int op) { Py_ssize_t len_self, len_other; int ok; PyObject *result; assert(self != NULL); assert(PyAnyDictViewSet_Check(self)); assert(other != NULL); if (!PyAnySet_Check(other) && !PyAnyDictViewSet_Check(other)) Py_RETURN_NOTIMPLEMENTED; len_self = PyObject_Size(self); if (len_self < 0) return NULL; len_other = PyObject_Size(other); if (len_other < 0) return NULL; ok = 0; switch(op) { case Py_NE: case Py_EQ: if (len_self == len_other) ok = all_contained_in(self, other); if (op == Py_NE && ok >= 0) ok = !ok; break; case Py_LT: if (len_self < len_other) ok = all_contained_in(self, other); break; case Py_LE: if (len_self <= len_other) ok = all_contained_in(self, other); break; case Py_GT: if (len_self > len_other) ok = all_contained_in(other, self); break; case Py_GE: if (len_self >= len_other) ok = all_contained_in(other, self); break; } if (ok < 0) return NULL; result = ok ? Py_True : Py_False; Py_INCREF(result); return result; } static PyObject * dictview_repr(_PyDictViewObject *dv) { PyObject *seq; PyObject *result = NULL; Py_ssize_t rc; rc = Py_ReprEnter((PyObject *)dv); if (rc != 0) { return rc > 0 ? PyUnicode_FromString("...") : NULL; } seq = PySequence_List((PyObject *)dv); if (seq == NULL) { goto Done; } result = PyUnicode_FromFormat("%s(%R)", Py_TYPE(dv)->tp_name, seq); Py_DECREF(seq); Done: Py_ReprLeave((PyObject *)dv); return result; } /*** dict_keys ***/ static int dictkeys_contains(_PyDictViewObject *dv, PyObject *obj) { if (dv->dv_dict == NULL) return 0; return PyDict_Contains((PyObject *)dv->dv_dict, obj); } static PySequenceMethods dictkeys_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictkeys_contains, /* sq_contains */ }; // Create an set object from dictviews object. // Returns a new reference. // This utility function is used by set operations. static PyObject* dictviews_to_set(PyObject *self) { PyObject *left = self; if (PyAnyDictKeys_Check(self)) { // PySet_New() has fast path for the dict object. PyObject *dict = (PyObject *)((_PyDictViewObject *)self)->dv_dict; if (PyAnyDict_CheckExact(dict)) { left = dict; } } return PySet_New(left); } static PyObject* dictviews_sub(PyObject* self, PyObject *other) { PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } _Py_IDENTIFIER(difference_update); PyObject *tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_difference_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static int dictitems_contains(_PyDictViewObject *dv, PyObject *obj); static PyObject* _d_PyDictView_Intersect(PyObject* self, PyObject *other) { PyObject *result; PyObject *it; PyObject *key; Py_ssize_t len_self; int rv; int (*dict_contains)(_PyDictViewObject *, PyObject *); /* Python interpreter swaps parameters when dict view is on right side of & */ if (!PyAnyDictViewSet_Check(self)) { PyObject *tmp = other; other = self; self = tmp; } len_self = dictview_len((_PyDictViewObject *)self); /* if other is a set and self is smaller than other, reuse set intersection logic */ if (PySet_CheckExact(other) && len_self <= PyObject_Size(other)) { _Py_IDENTIFIER(intersection); return _PyObject_CallMethodIdObjArgs(other, &PyId_intersection, self, NULL); } /* if other is another dict view, and it is bigger than self, swap them */ if (PyAnyDictViewSet_Check(other)) { Py_ssize_t len_other = dictview_len((_PyDictViewObject *)other); if (len_other > len_self) { PyObject *tmp = other; other = self; self = tmp; } } /* at this point, two things should be true 1. self is a dictview 2. if other is a dictview then it is smaller than self */ result = PySet_New(NULL); if (result == NULL) return NULL; it = PyObject_GetIter(other); if (it == NULL) { Py_DECREF(result); return NULL; } if (PyAnyDictKeys_Check(self)) { dict_contains = dictkeys_contains; } /* else PyDictItems_Check(self) */ else { dict_contains = dictitems_contains; } while ((key = PyIter_Next(it)) != NULL) { rv = dict_contains((_PyDictViewObject *)self, key); if (rv < 0) { goto error; } if (rv) { if (PySet_Add(result, key)) { goto error; } } Py_DECREF(key); } Py_DECREF(it); if (PyErr_Occurred()) { Py_DECREF(result); return NULL; } return result; error: Py_DECREF(it); Py_DECREF(result); Py_DECREF(key); return NULL; } static PyObject* dictviews_or(PyObject* self, PyObject *other) { PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } if (_PySet_Update(result, other) < 0) { Py_DECREF(result); return NULL; } return result; } static PyObject* dictitems_xor(PyObject *self, PyObject *other) { assert(PyAnyDictItems_Check(self)); assert(PyAnyDictItems_Check(other)); PyObject *d1 = (PyObject *)((_PyDictViewObject *)self)->dv_dict; PyObject *d2 = (PyObject *)((_PyDictViewObject *)other)->dv_dict; PyObject *temp_dict; if (PyDict_CheckExact(d1)) { temp_dict = PyDict_Copy(d1); } else { PyObject* args = PyTuple_New(1); if (args == NULL) { return NULL; } Py_INCREF(d1); PyTuple_SET_ITEM(args, 0, d1); temp_dict = PyObject_Call((PyObject *) &PyDict_Type, args, NULL); Py_DECREF(args); } if (temp_dict == NULL) { return NULL; } PyObject *result_set = PySet_New(NULL); if (result_set == NULL) { Py_CLEAR(temp_dict); return NULL; } PyObject *key = NULL, *val1 = NULL, *val2 = NULL; Py_ssize_t pos = 0; Py_hash_t hash; while (_d_PyDict_Next(d2, &pos, &key, &val2, &hash)) { Py_INCREF(key); Py_INCREF(val2); val1 = _PyDict_GetItem_KnownHash(temp_dict, key, hash); int to_delete; if (val1 == NULL) { if (PyErr_Occurred()) { goto error; } to_delete = 0; } else { Py_INCREF(val1); to_delete = PyObject_RichCompareBool(val1, val2, Py_EQ); if (to_delete < 0) { goto error; } } if (to_delete) { if (_PyDict_DelItem_KnownHash(temp_dict, key, hash) < 0) { goto error; } } else { PyObject *pair = PyTuple_Pack(2, key, val2); if (pair == NULL) { goto error; } if (PySet_Add(result_set, pair) < 0) { Py_DECREF(pair); goto error; } Py_DECREF(pair); } Py_DECREF(key); Py_XDECREF(val1); Py_DECREF(val2); } key = val1 = val2 = NULL; _Py_IDENTIFIER(items); PyObject *remaining_pairs = _PyObject_CallMethodIdObjArgs(temp_dict, &PyId_items, NULL); if (remaining_pairs == NULL) { goto error; } if (_PySet_Update(result_set, remaining_pairs) < 0) { Py_DECREF(remaining_pairs); goto error; } Py_DECREF(temp_dict); Py_DECREF(remaining_pairs); return result_set; error: Py_XDECREF(temp_dict); Py_XDECREF(result_set); Py_XDECREF(key); Py_XDECREF(val1); Py_XDECREF(val2); return NULL; } static PyObject* dictviews_xor(PyObject* self, PyObject *other) { if (PyAnyDictItems_Check(self) && PyAnyDictItems_Check(other)) { return dictitems_xor(self, other); } PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } _Py_IDENTIFIER(symmetric_difference_update); PyObject *tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_symmetric_difference_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static PyNumberMethods dictviews_as_number = { 0, /*nb_add*/ (binaryfunc)dictviews_sub, /*nb_subtract*/ 0, /*nb_multiply*/ 0, /*nb_remainder*/ 0, /*nb_divmod*/ 0, /*nb_power*/ 0, /*nb_negative*/ 0, /*nb_positive*/ 0, /*nb_absolute*/ 0, /*nb_bool*/ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ (binaryfunc)_d_PyDictView_Intersect,/*nb_and*/ (binaryfunc)dictviews_xor, /*nb_xor*/ (binaryfunc)dictviews_or, /*nb_or*/ }; static PyObject* dictviews_isdisjoint(PyObject *self, PyObject *other) { PyObject *it; PyObject *item = NULL; if (self == other) { if (dictview_len((_PyDictViewObject *)self) == 0) Py_RETURN_TRUE; else Py_RETURN_FALSE; } /* Iterate over the shorter object (only if other is a set, * because PySequence_Contains may be expensive otherwise): */ if (PyAnySet_Check(other) || PyAnyDictViewSet_Check(other)) { Py_ssize_t len_self = dictview_len((_PyDictViewObject *)self); Py_ssize_t len_other = PyObject_Size(other); if (len_other == -1) return NULL; if ((len_other > len_self)) { PyObject *tmp = other; other = self; self = tmp; } } it = PyObject_GetIter(other); if (it == NULL) return NULL; while ((item = PyIter_Next(it)) != NULL) { int contains = PySequence_Contains(self, item); Py_DECREF(item); if (contains == -1) { Py_DECREF(it); return NULL; } if (contains) { Py_DECREF(it); Py_RETURN_FALSE; } } Py_DECREF(it); if (PyErr_Occurred()) return NULL; /* PyIter_Next raised an exception. */ Py_RETURN_TRUE; } PyDoc_STRVAR(isdisjoint_doc, "Return True if the view and the given iterable have a null intersection."); static PyObject* dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_keys_doc, "Return a reverse iterator over the dict keys."); static PyMethodDef dictkeys_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {"__reversed__", (PyCFunction)(void(*)(void))dictkeys_reversed, METH_NOARGS, reversed_keys_doc}, {NULL, NULL} /* sentinel */ }; static PyObject * dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterKey_Type); } /*** dict_items ***/ /* Variant of PyDict_GetItem() that doesn't suppress exceptions. This returns NULL *with* an exception set if an exception occurred. It returns NULL *without* an exception set if the key wasn't present. */ static PyObject * d_PyDict_GetItemWithError(PyObject *op, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyDictObject*mp = (PyDictObject *)op; PyObject **value_addr; if (!PyAnyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { return NULL; } } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL); if (ix < 0) return NULL; return *value_addr; } static int dictitems_contains(_PyDictViewObject *dv, PyObject *obj) { int result; PyObject *key, *value, *found; if (dv->dv_dict == NULL) return 0; if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) return 0; key = PyTuple_GET_ITEM(obj, 0); value = PyTuple_GET_ITEM(obj, 1); found = d_PyDict_GetItemWithError((PyObject *)dv->dv_dict, key); if (found == NULL) { if (PyErr_Occurred()) return -1; return 0; } Py_INCREF(found); result = PyObject_RichCompareBool(found, value, Py_EQ); Py_DECREF(found); return result; } static PySequenceMethods dictitems_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictitems_contains, /* sq_contains */ }; static PyObject* dictitems_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_items_doc, "Return a reverse iterator over the dict items."); static PyMethodDef dictitems_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {"__reversed__", (PyCFunction)dictitems_reversed, METH_NOARGS, reversed_items_doc}, {NULL, NULL} /* sentinel */ }; static PyObject * dictitems_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterItem_Type); } /*** dict_values ***/ static PySequenceMethods dictvalues_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)0, /* sq_contains */ }; static PyObject* dictvalues_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_values_doc, "Return a reverse iterator over the dict values."); static PyMethodDef dictvalues_methods[] = { {"__reversed__", (PyCFunction)dictvalues_reversed, METH_NOARGS, reversed_values_doc}, {NULL, NULL} /* sentinel */ }; static PyObject * dictvalues_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterValue_Type); } python-frozendict/src/frozendict/c_src/3_6/cpython_src/Objects/dictobject_original.c000066400000000000000000004076251470250572200312720ustar00rootroot00000000000000/* Dictionary object implementation using a hash table */ /* The distribution includes a separate file, Objects/dictnotes.txt, describing explorations into dictionary design and optimization. It covers typical dictionary use patterns, the parameters for tuning dictionaries, and several ideas for possible optimizations. */ /* PyDictKeysObject This implements the dictionary's hashtable. As of Python 3.6, this is compact and ordered. Basic idea is described here. https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html layout: +---------------+ | dk_refcnt | | dk_size | | dk_lookup | | dk_usable | | dk_nentries | +---------------+ | dk_indices | | | +---------------+ | dk_entries | | | +---------------+ dk_indices is actual hashtable. It holds index in entries, or DKIX_EMPTY(-1) or DKIX_DUMMY(-2). Size of indices is dk_size. Type of each index in indices is vary on dk_size: * int8 for dk_size <= 128 * int16 for 256 <= dk_size <= 2**15 * int32 for 2**16 <= dk_size <= 2**31 * int64 for 2**32 <= dk_size dk_entries is array of PyDictKeyEntry. It's size is USABLE_FRACTION(dk_size). DK_ENTRIES(dk) can be used to get pointer to entries. NOTE: Since negative value is used for DKIX_EMPTY and DKIX_DUMMY, type of dk_indices entry is signed integer and int16 is used for table which dk_size == 256. */ /* The DictObject can be in one of two forms. Either: A combined table: ma_values == NULL, dk_refcnt == 1. Values are stored in the me_value field of the PyDictKeysObject. Or: A split table: ma_values != NULL, dk_refcnt >= 1 Values are stored in the ma_values array. Only string (unicode) keys are allowed. All dicts sharing same key must have same insertion order. There are four kinds of slots in the table (slot is index, and DK_ENTRIES(keys)[index] if index >= 0): 1. Unused. index == DKIX_EMPTY Does not hold an active (key, value) pair now and never did. Unused can transition to Active upon key insertion. This is each slot's initial state. 2. Active. index >= 0, me_key != NULL and me_value != NULL Holds an active (key, value) pair. Active can transition to Dummy or Pending upon key deletion (for combined and split tables respectively). This is the only case in which me_value != NULL. 3. Dummy. index == DKIX_DUMMY (combined only) Previously held an active (key, value) pair, but that was deleted and an active pair has not yet overwritten the slot. Dummy can transition to Active upon key insertion. Dummy slots cannot be made Unused again else the probe sequence in case of collision would have no way to know they were once active. 4. Pending. index >= 0, key != NULL, and value == NULL (split only) Not yet inserted in split-table. */ /* Preserving insertion order It's simple for combined table. Since dk_entries is mostly append only, we can get insertion order by just iterating dk_entries. One exception is .popitem(). It removes last item in dk_entries and decrement dk_nentries to achieve amortized O(1). Since there are DKIX_DUMMY remains in dk_indices, we can't increment dk_usable even though dk_nentries is decremented. In split table, inserting into pending entry is allowed only for dk_entries[ix] where ix == mp->ma_used. Inserting into other index and deleting item cause converting the dict to the combined table. */ /* PyDict_MINSIZE is the starting size for any new dict. * 8 allows dicts with no more than 5 active entries; experiments suggested * this suffices for the majority of dicts (consisting mostly of usually-small * dicts created to pass keyword arguments). * Making this 8, rather than 4 reduces the number of resizes for most * dictionaries, without any significant extra memory use. */ #define PyDict_MINSIZE 8 #include "Python.h" #include "dict-common.h" #include "stringlib/eq.h" /* to get unicode_eq() */ /*[clinic input] class dict "PyDictObject *" "&PyDict_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=f157a5a0ce9589d6]*/ /* To ensure the lookup algorithm terminates, there must be at least one Unused slot (NULL key) in the table. To avoid slowing down lookups on a near-full table, we resize the table when it's USABLE_FRACTION (currently two-thirds) full. */ #define PERTURB_SHIFT 5 /* Major subtleties ahead: Most hash schemes depend on having a "good" hash function, in the sense of simulating randomness. Python doesn't: its most important hash functions (for ints) are very regular in common cases: >>>[hash(i) for i in range(4)] [0, 1, 2, 3] This isn't necessarily bad! To the contrary, in a table of size 2**i, taking the low-order i bits as the initial table index is extremely fast, and there are no collisions at all for dicts indexed by a contiguous range of ints. So this gives better-than-random behavior in common cases, and that's very desirable. OTOH, when collisions occur, the tendency to fill contiguous slices of the hash table makes a good collision resolution strategy crucial. Taking only the last i bits of the hash code is also vulnerable: for example, consider the list [i << 16 for i in range(20000)] as a set of keys. Since ints are their own hash codes, and this fits in a dict of size 2**15, the last 15 bits of every hash code are all 0: they *all* map to the same table index. But catering to unusual cases should not slow the usual ones, so we just take the last i bits anyway. It's up to collision resolution to do the rest. If we *usually* find the key we're looking for on the first try (and, it turns out, we usually do -- the table load factor is kept under 2/3, so the odds are solidly in our favor), then it makes best sense to keep the initial index computation dirt cheap. The first half of collision resolution is to visit table indices via this recurrence: j = ((5*j) + 1) mod 2**i For any initial j in range(2**i), repeating that 2**i times generates each int in range(2**i) exactly once (see any text on random-number generation for proof). By itself, this doesn't help much: like linear probing (setting j += 1, or j -= 1, on each loop trip), it scans the table entries in a fixed order. This would be bad, except that's not the only thing we do, and it's actually *good* in the common cases where hash keys are consecutive. In an example that's really too small to make this entirely clear, for a table of size 2**3 the order of indices is: 0 -> 1 -> 6 -> 7 -> 4 -> 5 -> 2 -> 3 -> 0 [and here it's repeating] If two things come in at index 5, the first place we look after is index 2, not 6, so if another comes in at index 6 the collision at 5 didn't hurt it. Linear probing is deadly in this case because there the fixed probe order is the *same* as the order consecutive keys are likely to arrive. But it's extremely unlikely hash codes will follow a 5*j+1 recurrence by accident, and certain that consecutive hash codes do not. The other half of the strategy is to get the other bits of the hash code into play. This is done by initializing a (unsigned) vrbl "perturb" to the full hash code, and changing the recurrence to: perturb >>= PERTURB_SHIFT; j = (5*j) + 1 + perturb; use j % 2**i as the next table index; Now the probe sequence depends (eventually) on every bit in the hash code, and the pseudo-scrambling property of recurring on 5*j+1 is more valuable, because it quickly magnifies small differences in the bits that didn't affect the initial index. Note that because perturb is unsigned, if the recurrence is executed often enough perturb eventually becomes and remains 0. At that point (very rarely reached) the recurrence is on (just) 5*j+1 again, and that's certain to find an empty slot eventually (since it generates every int in range(2**i), and we make sure there's always at least one empty slot). Selecting a good value for PERTURB_SHIFT is a balancing act. You want it small so that the high bits of the hash code continue to affect the probe sequence across iterations; but you want it large so that in really bad cases the high-order hash bits have an effect on early iterations. 5 was "the best" in minimizing total collisions across experiments Tim Peters ran (on both normal and pathological cases), but 4 and 6 weren't significantly worse. Historical: Reimer Behrends contributed the idea of using a polynomial-based approach, using repeated multiplication by x in GF(2**n) where an irreducible polynomial for each table size was chosen such that x was a primitive root. Christian Tismer later extended that to use division by x instead, as an efficient way to get the high bits of the hash code into play. This scheme also gave excellent collision statistics, but was more expensive: two if-tests were required inside the loop; computing "the next" index took about the same number of operations but without as much potential parallelism (e.g., computing 5*j can go on at the same time as computing 1+perturb in the above, and then shifting perturb can be done while the table index is being masked); and the PyDictObject struct required a member to hold the table's polynomial. In Tim's experiments the current scheme ran faster, produced equally good collision statistics, needed less code & used less memory. */ /* forward declarations */ static Py_ssize_t lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos); static Py_ssize_t lookdict_unicode(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos); static Py_ssize_t lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos); static Py_ssize_t lookdict_split(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos); static int dictresize(PyDictObject *mp, Py_ssize_t minused); static PyObject* dict_iter(PyDictObject *dict); /*Global counter used to set ma_version_tag field of dictionary. * It is incremented each time that a dictionary is created and each * time that a dictionary is modified. */ static uint64_t pydict_global_version = 0; #define DICT_NEXT_VERSION() (++pydict_global_version) /* Dictionary reuse scheme to save calls to malloc and free */ #ifndef PyDict_MAXFREELIST #define PyDict_MAXFREELIST 80 #endif static PyDictObject *free_list[PyDict_MAXFREELIST]; static int numfree = 0; static PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST]; static int numfreekeys = 0; #include "clinic/dictobject.c.h" int PyDict_ClearFreeList(void) { PyDictObject *op; int ret = numfree + numfreekeys; while (numfree) { op = free_list[--numfree]; assert(PyDict_CheckExact(op)); PyObject_GC_Del(op); } while (numfreekeys) { PyObject_FREE(keys_free_list[--numfreekeys]); } return ret; } /* Print summary info about the state of the optimized allocator */ void _PyDict_DebugMallocStats(FILE *out) { _PyDebugAllocatorStats(out, "free PyDictObject", numfree, sizeof(PyDictObject)); } void PyDict_Fini(void) { PyDict_ClearFreeList(); } #define DK_SIZE(dk) ((dk)->dk_size) #if SIZEOF_VOID_P > 4 #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : DK_SIZE(dk) <= 0xffffffff ? \ 4 : sizeof(int64_t)) #else #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : sizeof(int32_t)) #endif #define DK_ENTRIES(dk) \ ((PyDictKeyEntry*)(&((int8_t*)((dk)->dk_indices))[DK_SIZE(dk) * DK_IXSIZE(dk)])) #define DK_DEBUG_INCREF _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA #define DK_DEBUG_DECREF _Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA #define DK_INCREF(dk) (DK_DEBUG_INCREF ++(dk)->dk_refcnt) #define DK_DECREF(dk) if (DK_DEBUG_DECREF (--(dk)->dk_refcnt) == 0) free_keys_object(dk) #define DK_MASK(dk) (((dk)->dk_size)-1) #define IS_POWER_OF_2(x) (((x) & (x-1)) == 0) /* lookup indices. returns DKIX_EMPTY, DKIX_DUMMY, or ix >=0 */ static inline Py_ssize_t dk_get_index(PyDictKeysObject *keys, Py_ssize_t i) { Py_ssize_t s = DK_SIZE(keys); Py_ssize_t ix; if (s <= 0xff) { int8_t *indices = (int8_t*)(keys->dk_indices); ix = indices[i]; } else if (s <= 0xffff) { int16_t *indices = (int16_t*)(keys->dk_indices); ix = indices[i]; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { int64_t *indices = (int64_t*)(keys->dk_indices); ix = indices[i]; } #endif else { int32_t *indices = (int32_t*)(keys->dk_indices); ix = indices[i]; } assert(ix >= DKIX_DUMMY); return ix; } /* write to indices. */ static inline void dk_set_index(PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix) { Py_ssize_t s = DK_SIZE(keys); assert(ix >= DKIX_DUMMY); if (s <= 0xff) { int8_t *indices = (int8_t*)(keys->dk_indices); assert(ix <= 0x7f); indices[i] = (char)ix; } else if (s <= 0xffff) { int16_t *indices = (int16_t*)(keys->dk_indices); assert(ix <= 0x7fff); indices[i] = (int16_t)ix; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { int64_t *indices = (int64_t*)(keys->dk_indices); indices[i] = ix; } #endif else { int32_t *indices = (int32_t*)(keys->dk_indices); assert(ix <= 0x7fffffff); indices[i] = (int32_t)ix; } } /* USABLE_FRACTION is the maximum dictionary load. * Increasing this ratio makes dictionaries more dense resulting in more * collisions. Decreasing it improves sparseness at the expense of spreading * indices over more cache lines and at the cost of total memory consumed. * * USABLE_FRACTION must obey the following: * (0 < USABLE_FRACTION(n) < n) for all n >= 2 * * USABLE_FRACTION should be quick to calculate. * Fractions around 1/2 to 2/3 seem to work well in practice. */ #define USABLE_FRACTION(n) (((n) << 1)/3) /* ESTIMATE_SIZE is reverse function of USABLE_FRACTION. * This can be used to reserve enough size to insert n entries without * resizing. */ #define ESTIMATE_SIZE(n) (((n)*3+1) >> 1) /* Alternative fraction that is otherwise close enough to 2n/3 to make * little difference. 8 * 2/3 == 8 * 5/8 == 5. 16 * 2/3 == 16 * 5/8 == 10. * 32 * 2/3 = 21, 32 * 5/8 = 20. * Its advantage is that it is faster to compute on machines with slow division. * #define USABLE_FRACTION(n) (((n) >> 1) + ((n) >> 2) - ((n) >> 3)) */ /* GROWTH_RATE. Growth rate upon hitting maximum load. * Currently set to used*2 + capacity/2. * This means that dicts double in size when growing without deletions, * but have more head room when the number of deletions is on a par with the * number of insertions. * Raising this to used*4 doubles memory consumption depending on the size of * the dictionary, but results in half the number of resizes, less effort to * resize. * GROWTH_RATE was set to used*4 up to version 3.2. * GROWTH_RATE was set to used*2 in version 3.3.0 */ #define GROWTH_RATE(d) (((d)->ma_used*2)+((d)->ma_keys->dk_size>>1)) #define ENSURE_ALLOWS_DELETIONS(d) \ if ((d)->ma_keys->dk_lookup == lookdict_unicode_nodummy) { \ (d)->ma_keys->dk_lookup = lookdict_unicode; \ } /* This immutable, empty PyDictKeysObject is used for PyDict_Clear() * (which cannot fail and thus can do no allocation). */ static PyDictKeysObject empty_keys_struct = { 1, /* dk_refcnt */ 1, /* dk_size */ lookdict_split, /* dk_lookup */ 0, /* dk_usable (immutable) */ 0, /* dk_nentries */ {DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY}, /* dk_indices */ }; static PyObject *empty_values[1] = { NULL }; #define Py_EMPTY_KEYS &empty_keys_struct /* Uncomment to check the dict content in _PyDict_CheckConsistency() */ /* #define DEBUG_PYDICT */ #ifndef NDEBUG static int _PyDict_CheckConsistency(PyDictObject *mp) { PyDictKeysObject *keys = mp->ma_keys; int splitted = _PyDict_HasSplitTable(mp); Py_ssize_t usable = USABLE_FRACTION(keys->dk_size); #ifdef DEBUG_PYDICT PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i; #endif assert(0 <= mp->ma_used && mp->ma_used <= usable); assert(IS_POWER_OF_2(keys->dk_size)); assert(0 <= keys->dk_usable && keys->dk_usable <= usable); assert(0 <= keys->dk_nentries && keys->dk_nentries <= usable); assert(keys->dk_usable + keys->dk_nentries <= usable); if (!splitted) { /* combined table */ assert(keys->dk_refcnt == 1); } #ifdef DEBUG_PYDICT for (i=0; i < keys->dk_size; i++) { Py_ssize_t ix = dk_get_index(keys, i); assert(DKIX_DUMMY <= ix && ix <= usable); } for (i=0; i < usable; i++) { PyDictKeyEntry *entry = &entries[i]; PyObject *key = entry->me_key; if (key != NULL) { if (PyUnicode_CheckExact(key)) { Py_hash_t hash = ((PyASCIIObject *)key)->hash; assert(hash != -1); assert(entry->me_hash == hash); } else { /* test_dict fails if PyObject_Hash() is called again */ assert(entry->me_hash != -1); } if (!splitted) { assert(entry->me_value != NULL); } } if (splitted) { assert(entry->me_value == NULL); } } if (splitted) { /* splitted table */ for (i=0; i < mp->ma_used; i++) { assert(mp->ma_values[i] != NULL); } } #endif return 1; } #endif static PyDictKeysObject *new_keys_object(Py_ssize_t size) { PyDictKeysObject *dk; Py_ssize_t es, usable; assert(size >= PyDict_MINSIZE); assert(IS_POWER_OF_2(size)); usable = USABLE_FRACTION(size); if (size <= 0xff) { es = 1; } else if (size <= 0xffff) { es = 2; } #if SIZEOF_VOID_P > 4 else if (size <= 0xffffffff) { es = 4; } #endif else { es = sizeof(Py_ssize_t); } if (size == PyDict_MINSIZE && numfreekeys > 0) { dk = keys_free_list[--numfreekeys]; } else { dk = PyObject_MALLOC(sizeof(PyDictKeysObject) + es * size + sizeof(PyDictKeyEntry) * usable); if (dk == NULL) { PyErr_NoMemory(); return NULL; } } DK_DEBUG_INCREF dk->dk_refcnt = 1; dk->dk_size = size; dk->dk_usable = usable; dk->dk_lookup = lookdict_unicode_nodummy; dk->dk_nentries = 0; memset(&dk->dk_indices[0], 0xff, es * size); memset(DK_ENTRIES(dk), 0, sizeof(PyDictKeyEntry) * usable); return dk; } static void free_keys_object(PyDictKeysObject *keys) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n; for (i = 0, n = keys->dk_nentries; i < n; i++) { Py_XDECREF(entries[i].me_key); Py_XDECREF(entries[i].me_value); } if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) { keys_free_list[numfreekeys++] = keys; return; } PyObject_FREE(keys); } #define new_values(size) PyMem_NEW(PyObject *, size) #define free_values(values) PyMem_FREE(values) /* Consumes a reference to the keys object */ static PyObject * new_dict(PyDictKeysObject *keys, PyObject **values) { PyDictObject *mp; assert(keys != NULL); if (numfree) { mp = free_list[--numfree]; assert (mp != NULL); assert (Py_TYPE(mp) == &PyDict_Type); _Py_NewReference((PyObject *)mp); } else { mp = PyObject_GC_New(PyDictObject, &PyDict_Type); if (mp == NULL) { DK_DECREF(keys); free_values(values); return NULL; } } mp->ma_keys = keys; mp->ma_values = values; mp->ma_used = 0; mp->ma_version_tag = DICT_NEXT_VERSION(); assert(_PyDict_CheckConsistency(mp)); return (PyObject *)mp; } /* Consumes a reference to the keys object */ static PyObject * new_dict_with_shared_keys(PyDictKeysObject *keys) { PyObject **values; Py_ssize_t i, size; size = USABLE_FRACTION(DK_SIZE(keys)); values = new_values(size); if (values == NULL) { DK_DECREF(keys); return PyErr_NoMemory(); } for (i = 0; i < size; i++) { values[i] = NULL; } return new_dict(keys, values); } PyObject * PyDict_New(void) { PyDictKeysObject *keys = new_keys_object(PyDict_MINSIZE); if (keys == NULL) return NULL; return new_dict(keys, NULL); } /* Search index of hash table from offset of entry table */ static Py_ssize_t lookdict_index(PyDictKeysObject *k, Py_hash_t hash, Py_ssize_t index) { size_t i; size_t mask = DK_MASK(k); Py_ssize_t ix; i = (size_t)hash & mask; ix = dk_get_index(k, i); if (ix == index) { return i; } if (ix == DKIX_EMPTY) { return DKIX_EMPTY; } for (size_t perturb = hash;;) { perturb >>= PERTURB_SHIFT; i = mask & ((i << 2) + i + perturb + 1); ix = dk_get_index(k, i); if (ix == index) { return i; } if (ix == DKIX_EMPTY) { return DKIX_EMPTY; } } assert(0); /* NOT REACHED */ return DKIX_ERROR; } /* The basic lookup function used by all operations. This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4. Open addressing is preferred over chaining since the link overhead for chaining would be substantial (100% with typical malloc overhead). The initial probe index is computed as hash mod the table size. Subsequent probe indices are computed as explained earlier. All arithmetic on hash should ignore overflow. The details in this version are due to Tim Peters, building on many past contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and Christian Tismer. lookdict() is general-purpose, and may return DKIX_ERROR if (and only if) a comparison raises an exception. lookdict_unicode() below is specialized to string keys, comparison of which can never raise an exception; that function can never return DKIX_ERROR when key is string. Otherwise, it falls back to lookdict(). lookdict_unicode_nodummy is further specialized for string keys that cannot be the value. For both, when the key isn't found a DKIX_EMPTY is returned. hashpos returns where the key index should be inserted. */ static Py_ssize_t lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos) { size_t i, mask; Py_ssize_t ix, freeslot; int cmp; PyDictKeysObject *dk; PyDictKeyEntry *ep0, *ep; PyObject *startkey; top: dk = mp->ma_keys; mask = DK_MASK(dk); ep0 = DK_ENTRIES(dk); i = (size_t)hash & mask; ix = dk_get_index(dk, i); if (ix == DKIX_EMPTY) { if (hashpos != NULL) *hashpos = i; *value_addr = NULL; return DKIX_EMPTY; } if (ix == DKIX_DUMMY) { freeslot = i; } else { ep = &ep0[ix]; assert(ep->me_key != NULL); if (ep->me_key == key) { *value_addr = &ep->me_value; if (hashpos != NULL) *hashpos = i; return ix; } if (ep->me_hash == hash) { startkey = ep->me_key; Py_INCREF(startkey); cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); Py_DECREF(startkey); if (cmp < 0) { *value_addr = NULL; return DKIX_ERROR; } if (dk == mp->ma_keys && ep->me_key == startkey) { if (cmp > 0) { *value_addr = &ep->me_value; if (hashpos != NULL) *hashpos = i; return ix; } } else { /* The dict was mutated, restart */ goto top; } } freeslot = -1; } for (size_t perturb = hash;;) { perturb >>= PERTURB_SHIFT; i = ((i << 2) + i + perturb + 1) & mask; ix = dk_get_index(dk, i); if (ix == DKIX_EMPTY) { if (hashpos != NULL) { *hashpos = (freeslot == -1) ? (Py_ssize_t)i : freeslot; } *value_addr = NULL; return ix; } if (ix == DKIX_DUMMY) { if (freeslot == -1) freeslot = i; continue; } ep = &ep0[ix]; assert(ep->me_key != NULL); if (ep->me_key == key) { if (hashpos != NULL) { *hashpos = i; } *value_addr = &ep->me_value; return ix; } if (ep->me_hash == hash) { startkey = ep->me_key; Py_INCREF(startkey); cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); Py_DECREF(startkey); if (cmp < 0) { *value_addr = NULL; return DKIX_ERROR; } if (dk == mp->ma_keys && ep->me_key == startkey) { if (cmp > 0) { if (hashpos != NULL) { *hashpos = i; } *value_addr = &ep->me_value; return ix; } } else { /* The dict was mutated, restart */ goto top; } } } assert(0); /* NOT REACHED */ return 0; } /* Specialized version for string-only keys */ static Py_ssize_t lookdict_unicode(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos) { size_t i; size_t mask = DK_MASK(mp->ma_keys); Py_ssize_t ix, freeslot; PyDictKeyEntry *ep, *ep0 = DK_ENTRIES(mp->ma_keys); assert(mp->ma_values == NULL); /* Make sure this function doesn't have to handle non-unicode keys, including subclasses of str; e.g., one reason to subclass unicodes is to override __eq__, and for speed we don't cater to that here. */ if (!PyUnicode_CheckExact(key)) { mp->ma_keys->dk_lookup = lookdict; return lookdict(mp, key, hash, value_addr, hashpos); } i = (size_t)hash & mask; ix = dk_get_index(mp->ma_keys, i); if (ix == DKIX_EMPTY) { if (hashpos != NULL) *hashpos = i; *value_addr = NULL; return DKIX_EMPTY; } if (ix == DKIX_DUMMY) { freeslot = i; } else { ep = &ep0[ix]; assert(ep->me_key != NULL); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { if (hashpos != NULL) *hashpos = i; *value_addr = &ep->me_value; return ix; } freeslot = -1; } for (size_t perturb = hash;;) { perturb >>= PERTURB_SHIFT; i = mask & ((i << 2) + i + perturb + 1); ix = dk_get_index(mp->ma_keys, i); if (ix == DKIX_EMPTY) { if (hashpos != NULL) { *hashpos = (freeslot == -1) ? (Py_ssize_t)i : freeslot; } *value_addr = NULL; return DKIX_EMPTY; } if (ix == DKIX_DUMMY) { if (freeslot == -1) freeslot = i; continue; } ep = &ep0[ix]; assert(ep->me_key != NULL); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { *value_addr = &ep->me_value; if (hashpos != NULL) { *hashpos = i; } return ix; } } assert(0); /* NOT REACHED */ return 0; } /* Faster version of lookdict_unicode when it is known that no keys * will be present. */ static Py_ssize_t lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos) { size_t i; size_t mask = DK_MASK(mp->ma_keys); Py_ssize_t ix; PyDictKeyEntry *ep, *ep0 = DK_ENTRIES(mp->ma_keys); assert(mp->ma_values == NULL); /* Make sure this function doesn't have to handle non-unicode keys, including subclasses of str; e.g., one reason to subclass unicodes is to override __eq__, and for speed we don't cater to that here. */ if (!PyUnicode_CheckExact(key)) { mp->ma_keys->dk_lookup = lookdict; return lookdict(mp, key, hash, value_addr, hashpos); } i = (size_t)hash & mask; ix = dk_get_index(mp->ma_keys, i); assert (ix != DKIX_DUMMY); if (ix == DKIX_EMPTY) { if (hashpos != NULL) *hashpos = i; *value_addr = NULL; return DKIX_EMPTY; } ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { if (hashpos != NULL) *hashpos = i; *value_addr = &ep->me_value; return ix; } for (size_t perturb = hash;;) { perturb >>= PERTURB_SHIFT; i = mask & ((i << 2) + i + perturb + 1); ix = dk_get_index(mp->ma_keys, i); assert (ix != DKIX_DUMMY); if (ix == DKIX_EMPTY) { if (hashpos != NULL) *hashpos = i; *value_addr = NULL; return DKIX_EMPTY; } ep = &ep0[ix]; assert(ep->me_key != NULL && PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { if (hashpos != NULL) *hashpos = i; *value_addr = &ep->me_value; return ix; } } assert(0); /* NOT REACHED */ return 0; } /* Version of lookdict for split tables. * All split tables and only split tables use this lookup function. * Split tables only contain unicode keys and no dummy keys, * so algorithm is the same as lookdict_unicode_nodummy. */ static Py_ssize_t lookdict_split(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos) { size_t i; size_t mask = DK_MASK(mp->ma_keys); Py_ssize_t ix; PyDictKeyEntry *ep, *ep0 = DK_ENTRIES(mp->ma_keys); /* mp must split table */ assert(mp->ma_values != NULL); if (!PyUnicode_CheckExact(key)) { ix = lookdict(mp, key, hash, value_addr, hashpos); if (ix >= 0) { *value_addr = &mp->ma_values[ix]; } return ix; } i = (size_t)hash & mask; ix = dk_get_index(mp->ma_keys, i); if (ix == DKIX_EMPTY) { if (hashpos != NULL) *hashpos = i; *value_addr = NULL; return DKIX_EMPTY; } assert(ix >= 0); ep = &ep0[ix]; assert(ep->me_key != NULL && PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { if (hashpos != NULL) *hashpos = i; *value_addr = &mp->ma_values[ix]; return ix; } for (size_t perturb = hash;;) { perturb >>= PERTURB_SHIFT; i = mask & ((i << 2) + i + perturb + 1); ix = dk_get_index(mp->ma_keys, i); if (ix == DKIX_EMPTY) { if (hashpos != NULL) *hashpos = i; *value_addr = NULL; return DKIX_EMPTY; } assert(ix >= 0); ep = &ep0[ix]; assert(ep->me_key != NULL && PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { if (hashpos != NULL) *hashpos = i; *value_addr = &mp->ma_values[ix]; return ix; } } assert(0); /* NOT REACHED */ return 0; } int _PyDict_HasOnlyStringKeys(PyObject *dict) { Py_ssize_t pos = 0; PyObject *key, *value; assert(PyDict_Check(dict)); /* Shortcut */ if (((PyDictObject *)dict)->ma_keys->dk_lookup != lookdict) return 1; while (PyDict_Next(dict, &pos, &key, &value)) if (!PyUnicode_Check(key)) return 0; return 1; } #define MAINTAIN_TRACKING(mp, key, value) \ do { \ if (!_PyObject_GC_IS_TRACKED(mp)) { \ if (_PyObject_GC_MAY_BE_TRACKED(key) || \ _PyObject_GC_MAY_BE_TRACKED(value)) { \ _PyObject_GC_TRACK(mp); \ } \ } \ } while(0) void _PyDict_MaybeUntrack(PyObject *op) { PyDictObject *mp; PyObject *value; Py_ssize_t i, numentries; PyDictKeyEntry *ep0; if (!PyDict_CheckExact(op) || !_PyObject_GC_IS_TRACKED(op)) return; mp = (PyDictObject *) op; ep0 = DK_ENTRIES(mp->ma_keys); numentries = mp->ma_keys->dk_nentries; if (_PyDict_HasSplitTable(mp)) { for (i = 0; i < numentries; i++) { if ((value = mp->ma_values[i]) == NULL) continue; if (_PyObject_GC_MAY_BE_TRACKED(value)) { assert(!_PyObject_GC_MAY_BE_TRACKED(ep0[i].me_key)); return; } } } else { for (i = 0; i < numentries; i++) { if ((value = ep0[i].me_value) == NULL) continue; if (_PyObject_GC_MAY_BE_TRACKED(value) || _PyObject_GC_MAY_BE_TRACKED(ep0[i].me_key)) return; } } _PyObject_GC_UNTRACK(op); } /* Internal function to find slot for an item from its hash when it is known that the key is not present in the dict. The dict must be combined. */ static void find_empty_slot(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ***value_addr, Py_ssize_t *hashpos) { size_t i; size_t mask = DK_MASK(mp->ma_keys); Py_ssize_t ix; PyDictKeyEntry *ep, *ep0 = DK_ENTRIES(mp->ma_keys); assert(!_PyDict_HasSplitTable(mp)); assert(hashpos != NULL); assert(key != NULL); if (!PyUnicode_CheckExact(key)) mp->ma_keys->dk_lookup = lookdict; i = hash & mask; ix = dk_get_index(mp->ma_keys, i); for (size_t perturb = hash; ix != DKIX_EMPTY;) { perturb >>= PERTURB_SHIFT; i = (i << 2) + i + perturb + 1; ix = dk_get_index(mp->ma_keys, i & mask); } ep = &ep0[mp->ma_keys->dk_nentries]; *hashpos = i & mask; assert(ep->me_value == NULL); *value_addr = &ep->me_value; } static int insertion_resize(PyDictObject *mp) { return dictresize(mp, GROWTH_RATE(mp)); } /* Internal routine to insert a new item into the table. Used both by the internal resize routine and by the public insert routine. Returns -1 if an error occurred, or 0 on success. */ static int insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) { PyObject *old_value; PyObject **value_addr; PyDictKeyEntry *ep, *ep0; Py_ssize_t hashpos, ix; Py_INCREF(key); Py_INCREF(value); if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) { if (insertion_resize(mp) < 0) goto Fail; } ix = mp->ma_keys->dk_lookup(mp, key, hash, &value_addr, &hashpos); if (ix == DKIX_ERROR) goto Fail; assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict); MAINTAIN_TRACKING(mp, key, value); /* When insertion order is different from shared key, we can't share * the key anymore. Convert this instance to combine table. */ if (_PyDict_HasSplitTable(mp) && ((ix >= 0 && *value_addr == NULL && mp->ma_used != ix) || (ix == DKIX_EMPTY && mp->ma_used != mp->ma_keys->dk_nentries))) { if (insertion_resize(mp) < 0) goto Fail; find_empty_slot(mp, key, hash, &value_addr, &hashpos); ix = DKIX_EMPTY; } if (ix == DKIX_EMPTY) { /* Insert into new slot. */ if (mp->ma_keys->dk_usable <= 0) { /* Need to resize. */ if (insertion_resize(mp) < 0) goto Fail; find_empty_slot(mp, key, hash, &value_addr, &hashpos); } ep0 = DK_ENTRIES(mp->ma_keys); ep = &ep0[mp->ma_keys->dk_nentries]; dk_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries); ep->me_key = key; ep->me_hash = hash; if (mp->ma_values) { assert (mp->ma_values[mp->ma_keys->dk_nentries] == NULL); mp->ma_values[mp->ma_keys->dk_nentries] = value; } else { ep->me_value = value; } mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); mp->ma_keys->dk_usable--; mp->ma_keys->dk_nentries++; assert(mp->ma_keys->dk_usable >= 0); assert(_PyDict_CheckConsistency(mp)); return 0; } assert(value_addr != NULL); old_value = *value_addr; if (old_value != NULL) { *value_addr = value; mp->ma_version_tag = DICT_NEXT_VERSION(); assert(_PyDict_CheckConsistency(mp)); Py_DECREF(old_value); /* which **CAN** re-enter (see issue #22653) */ Py_DECREF(key); return 0; } /* pending state */ assert(_PyDict_HasSplitTable(mp)); assert(ix == mp->ma_used); *value_addr = value; mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); assert(_PyDict_CheckConsistency(mp)); Py_DECREF(key); return 0; Fail: Py_DECREF(value); Py_DECREF(key); return -1; } /* Internal routine used by dictresize() to insert an item which is known to be absent from the dict. This routine also assumes that the dict contains no deleted entries. Besides the performance benefit, using insertdict() in dictresize() is dangerous (SF bug #1456209). Note that no refcounts are changed by this routine; if needed, the caller is responsible for incref'ing `key` and `value`. Neither mp->ma_used nor k->dk_usable are modified by this routine; the caller must set them correctly */ static void insertdict_clean(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) { size_t i; PyDictKeysObject *k = mp->ma_keys; size_t mask = (size_t)DK_SIZE(k)-1; PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); PyDictKeyEntry *ep; assert(k->dk_lookup != NULL); assert(value != NULL); assert(key != NULL); assert(PyUnicode_CheckExact(key) || k->dk_lookup == lookdict); i = hash & mask; for (size_t perturb = hash; dk_get_index(k, i) != DKIX_EMPTY;) { perturb >>= PERTURB_SHIFT; i = mask & ((i << 2) + i + perturb + 1); } ep = &ep0[k->dk_nentries]; assert(ep->me_value == NULL); dk_set_index(k, i, k->dk_nentries); k->dk_nentries++; ep->me_key = key; ep->me_hash = hash; ep->me_value = value; } /* Restructure the table by allocating a new table and reinserting all items again. When entries have been deleted, the new table may actually be smaller than the old one. If a table is split (its keys and hashes are shared, its values are not), then the values are temporarily copied into the table, it is resized as a combined table, then the me_value slots in the old table are NULLed out. After resizing a table is always combined, but can be resplit by make_keys_shared(). */ static int dictresize(PyDictObject *mp, Py_ssize_t minsize) { Py_ssize_t i, newsize; PyDictKeysObject *oldkeys; PyObject **oldvalues; PyDictKeyEntry *ep0; /* Find the smallest table size > minused. */ for (newsize = PyDict_MINSIZE; newsize < minsize && newsize > 0; newsize <<= 1) ; if (newsize <= 0) { PyErr_NoMemory(); return -1; } oldkeys = mp->ma_keys; oldvalues = mp->ma_values; /* Allocate a new table. */ mp->ma_keys = new_keys_object(newsize); if (mp->ma_keys == NULL) { mp->ma_keys = oldkeys; return -1; } // New table must be large enough. assert(mp->ma_keys->dk_usable >= mp->ma_used); if (oldkeys->dk_lookup == lookdict) mp->ma_keys->dk_lookup = lookdict; mp->ma_values = NULL; ep0 = DK_ENTRIES(oldkeys); /* Main loop below assumes we can transfer refcount to new keys * and that value is stored in me_value. * Increment ref-counts and copy values here to compensate * This (resizing a split table) should be relatively rare */ if (oldvalues != NULL) { for (i = 0; i < oldkeys->dk_nentries; i++) { if (oldvalues[i] != NULL) { Py_INCREF(ep0[i].me_key); ep0[i].me_value = oldvalues[i]; } } } /* Main loop */ for (i = 0; i < oldkeys->dk_nentries; i++) { PyDictKeyEntry *ep = &ep0[i]; if (ep->me_value != NULL) { insertdict_clean(mp, ep->me_key, ep->me_hash, ep->me_value); } } mp->ma_keys->dk_usable -= mp->ma_used; if (oldvalues != NULL) { /* NULL out me_value slot in oldkeys, in case it was shared */ for (i = 0; i < oldkeys->dk_nentries; i++) ep0[i].me_value = NULL; DK_DECREF(oldkeys); if (oldvalues != empty_values) { free_values(oldvalues); } } else { assert(oldkeys->dk_lookup != lookdict_split); assert(oldkeys->dk_refcnt == 1); DK_DEBUG_DECREF PyObject_FREE(oldkeys); } return 0; } /* Returns NULL if unable to split table. * A NULL return does not necessarily indicate an error */ static PyDictKeysObject * make_keys_shared(PyObject *op) { Py_ssize_t i; Py_ssize_t size; PyDictObject *mp = (PyDictObject *)op; if (!PyDict_CheckExact(op)) return NULL; if (!_PyDict_HasSplitTable(mp)) { PyDictKeyEntry *ep0; PyObject **values; assert(mp->ma_keys->dk_refcnt == 1); if (mp->ma_keys->dk_lookup == lookdict) { return NULL; } else if (mp->ma_keys->dk_lookup == lookdict_unicode) { /* Remove dummy keys */ if (dictresize(mp, DK_SIZE(mp->ma_keys))) return NULL; } assert(mp->ma_keys->dk_lookup == lookdict_unicode_nodummy); /* Copy values into a new array */ ep0 = DK_ENTRIES(mp->ma_keys); size = USABLE_FRACTION(DK_SIZE(mp->ma_keys)); values = new_values(size); if (values == NULL) { PyErr_SetString(PyExc_MemoryError, "Not enough memory to allocate new values array"); return NULL; } for (i = 0; i < size; i++) { values[i] = ep0[i].me_value; ep0[i].me_value = NULL; } mp->ma_keys->dk_lookup = lookdict_split; mp->ma_values = values; } DK_INCREF(mp->ma_keys); return mp->ma_keys; } PyObject * _PyDict_NewPresized(Py_ssize_t minused) { const Py_ssize_t max_presize = 128 * 1024; Py_ssize_t newsize; PyDictKeysObject *new_keys; /* There are no strict guarantee that returned dict can contain minused * items without resize. So we create medium size dict instead of very * large dict or MemoryError. */ if (minused > USABLE_FRACTION(max_presize)) { newsize = max_presize; } else { Py_ssize_t minsize = ESTIMATE_SIZE(minused); newsize = PyDict_MINSIZE; while (newsize < minsize) { newsize <<= 1; } } assert(IS_POWER_OF_2(newsize)); new_keys = new_keys_object(newsize); if (new_keys == NULL) return NULL; return new_dict(new_keys, NULL); } /* Note that, for historical reasons, PyDict_GetItem() suppresses all errors * that may occur (originally dicts supported only string keys, and exceptions * weren't possible). So, while the original intent was that a NULL return * meant the key wasn't present, in reality it can mean that, or that an error * (suppressed) occurred while computing the key's hash, or that some error * (suppressed) occurred when comparing keys in the dict's internal probe * sequence. A nasty example of the latter is when a Python-coded comparison * function hits a stack-depth error, which can cause this to return NULL * even if the key is present. */ PyObject * PyDict_GetItem(PyObject *op, PyObject *key) { Py_hash_t hash; Py_ssize_t ix; PyDictObject *mp = (PyDictObject *)op; PyThreadState *tstate; PyObject **value_addr; if (!PyDict_Check(op)) return NULL; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { PyErr_Clear(); return NULL; } } /* We can arrive here with a NULL tstate during initialization: try running "python -Wi" for an example related to string interning. Let's just hope that no exception occurs then... This must be _PyThreadState_Current and not PyThreadState_GET() because in debug mode, the latter complains if tstate is NULL. */ tstate = _PyThreadState_UncheckedGet(); if (tstate != NULL && tstate->curexc_type != NULL) { /* preserve the existing exception */ PyObject *err_type, *err_value, *err_tb; PyErr_Fetch(&err_type, &err_value, &err_tb); ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL); /* ignore errors */ PyErr_Restore(err_type, err_value, err_tb); if (ix < 0) return NULL; } else { ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL); if (ix < 0) { PyErr_Clear(); return NULL; } } return *value_addr; } /* Same as PyDict_GetItemWithError() but with hash supplied by caller. This returns NULL *with* an exception set if an exception occurred. It returns NULL *without* an exception set if the key wasn't present. */ PyObject * _PyDict_GetItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) { Py_ssize_t ix; PyDictObject *mp = (PyDictObject *)op; PyObject **value_addr; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL); if (ix < 0) { return NULL; } return *value_addr; } /* Variant of PyDict_GetItem() that doesn't suppress exceptions. This returns NULL *with* an exception set if an exception occurred. It returns NULL *without* an exception set if the key wasn't present. */ PyObject * PyDict_GetItemWithError(PyObject *op, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyDictObject*mp = (PyDictObject *)op; PyObject **value_addr; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { return NULL; } } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL); if (ix < 0) return NULL; return *value_addr; } PyObject * _PyDict_GetItemIdWithError(PyObject *dp, struct _Py_Identifier *key) { PyObject *kv; kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) return NULL; return PyDict_GetItemWithError(dp, kv); } /* Fast version of global value lookup (LOAD_GLOBAL). * Lookup in globals, then builtins. * * Raise an exception and return NULL if an error occurred (ex: computing the * key hash failed, key comparison failed, ...). Return NULL if the key doesn't * exist. Return the value if the key exists. */ PyObject * _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyObject **value_addr; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } /* namespace 1: globals */ ix = globals->ma_keys->dk_lookup(globals, key, hash, &value_addr, NULL); if (ix == DKIX_ERROR) return NULL; if (ix != DKIX_EMPTY && *value_addr != NULL) return *value_addr; /* namespace 2: builtins */ ix = builtins->ma_keys->dk_lookup(builtins, key, hash, &value_addr, NULL); if (ix < 0) return NULL; return *value_addr; } /* CAUTION: PyDict_SetItem() must guarantee that it won't resize the * dictionary if it's merely replacing the value for an existing key. * This means that it's safe to loop over a dictionary with PyDict_Next() * and occasionally replace a value -- but you can't insert new keys or * remove them. */ int PyDict_SetItem(PyObject *op, PyObject *key, PyObject *value) { PyDictObject *mp; Py_hash_t hash; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); assert(value); mp = (PyDictObject *)op; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; } /* insertdict() handles any resizing that might be necessary */ return insertdict(mp, key, hash, value); } int _PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value, Py_hash_t hash) { PyDictObject *mp; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); assert(value); assert(hash != -1); mp = (PyDictObject *)op; /* insertdict() handles any resizing that might be necessary */ return insertdict(mp, key, hash, value); } static int delitem_common(PyDictObject *mp, Py_ssize_t hashpos, Py_ssize_t ix, PyObject **value_addr) { PyObject *old_key, *old_value; PyDictKeyEntry *ep; old_value = *value_addr; assert(old_value != NULL); *value_addr = NULL; mp->ma_used--; mp->ma_version_tag = DICT_NEXT_VERSION(); ep = &DK_ENTRIES(mp->ma_keys)[ix]; dk_set_index(mp->ma_keys, hashpos, DKIX_DUMMY); ENSURE_ALLOWS_DELETIONS(mp); old_key = ep->me_key; ep->me_key = NULL; Py_DECREF(old_key); Py_DECREF(old_value); assert(_PyDict_CheckConsistency(mp)); return 0; } int PyDict_DelItem(PyObject *op, PyObject *key) { Py_hash_t hash; assert(key); if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; } return _PyDict_DelItem_KnownHash(op, key, hash); } int _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) { Py_ssize_t hashpos, ix; PyDictObject *mp; PyObject **value_addr; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); assert(hash != -1); mp = (PyDictObject *)op; ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, &hashpos); if (ix == DKIX_ERROR) return -1; if (ix == DKIX_EMPTY || *value_addr == NULL) { _PyErr_SetKeyError(key); return -1; } assert(dk_get_index(mp->ma_keys, hashpos) == ix); // Split table doesn't allow deletion. Combine it. if (_PyDict_HasSplitTable(mp)) { if (dictresize(mp, DK_SIZE(mp->ma_keys))) { return -1; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, &hashpos); assert(ix >= 0); } return delitem_common(mp, hashpos, ix, value_addr); } /* This function promises that the predicate -> deletion sequence is atomic * (i.e. protected by the GIL), assuming the predicate itself doesn't * release the GIL. */ int _PyDict_DelItemIf(PyObject *op, PyObject *key, int (*predicate)(PyObject *value)) { Py_ssize_t hashpos, ix; PyDictObject *mp; Py_hash_t hash; PyObject **value_addr; int res; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); hash = PyObject_Hash(key); if (hash == -1) return -1; mp = (PyDictObject *)op; ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, &hashpos); if (ix == DKIX_ERROR) return -1; if (ix == DKIX_EMPTY || *value_addr == NULL) { _PyErr_SetKeyError(key); return -1; } assert(dk_get_index(mp->ma_keys, hashpos) == ix); // Split table doesn't allow deletion. Combine it. if (_PyDict_HasSplitTable(mp)) { if (dictresize(mp, DK_SIZE(mp->ma_keys))) { return -1; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, &hashpos); assert(ix >= 0); } res = predicate(*value_addr); if (res == -1) return -1; if (res > 0) return delitem_common(mp, hashpos, ix, value_addr); else return 0; } void PyDict_Clear(PyObject *op) { PyDictObject *mp; PyDictKeysObject *oldkeys; PyObject **oldvalues; Py_ssize_t i, n; if (!PyDict_Check(op)) return; mp = ((PyDictObject *)op); oldkeys = mp->ma_keys; oldvalues = mp->ma_values; if (oldvalues == empty_values) return; /* Empty the dict... */ DK_INCREF(Py_EMPTY_KEYS); mp->ma_keys = Py_EMPTY_KEYS; mp->ma_values = empty_values; mp->ma_used = 0; mp->ma_version_tag = DICT_NEXT_VERSION(); /* ...then clear the keys and values */ if (oldvalues != NULL) { n = oldkeys->dk_nentries; for (i = 0; i < n; i++) Py_CLEAR(oldvalues[i]); free_values(oldvalues); DK_DECREF(oldkeys); } else { assert(oldkeys->dk_refcnt == 1); DK_DECREF(oldkeys); } assert(_PyDict_CheckConsistency(mp)); } /* Internal version of PyDict_Next that returns a hash value in addition * to the key and value. * Return 1 on success, return 0 when the reached the end of the dictionary * (or if op is not a dictionary) */ int _PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue, Py_hash_t *phash) { Py_ssize_t i, n; PyDictObject *mp; PyDictKeyEntry *entry_ptr; PyObject *value; if (!PyDict_Check(op)) return 0; mp = (PyDictObject *)op; i = *ppos; n = mp->ma_keys->dk_nentries; if ((size_t)i >= (size_t)n) return 0; if (mp->ma_values) { PyObject **value_ptr = &mp->ma_values[i]; while (i < n && *value_ptr == NULL) { value_ptr++; i++; } if (i >= n) return 0; entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; value = *value_ptr; } else { entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) return 0; value = entry_ptr->me_value; } *ppos = i+1; if (pkey) *pkey = entry_ptr->me_key; if (phash) *phash = entry_ptr->me_hash; if (pvalue) *pvalue = value; return 1; } /* * Iterate over a dict. Use like so: * * Py_ssize_t i; * PyObject *key, *value; * i = 0; # important! i should not otherwise be changed by you * while (PyDict_Next(yourdict, &i, &key, &value)) { * Refer to borrowed references in key and value. * } * * Return 1 on success, return 0 when the reached the end of the dictionary * (or if op is not a dictionary) * * CAUTION: In general, it isn't safe to use PyDict_Next in a loop that * mutates the dict. One exception: it is safe if the loop merely changes * the values associated with the keys (but doesn't insert new keys or * delete keys), via PyDict_SetItem(). */ int PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) { return _PyDict_Next(op, ppos, pkey, pvalue, NULL); } /* Internal version of dict.pop(). */ PyObject * _PyDict_Pop_KnownHash(PyObject *dict, PyObject *key, Py_hash_t hash, PyObject *deflt) { Py_ssize_t ix, hashpos; PyObject *old_value, *old_key; PyDictKeyEntry *ep; PyObject **value_addr; PyDictObject *mp; assert(PyDict_Check(dict)); mp = (PyDictObject *)dict; if (mp->ma_used == 0) { if (deflt) { Py_INCREF(deflt); return deflt; } _PyErr_SetKeyError(key); return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, &hashpos); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || *value_addr == NULL) { if (deflt) { Py_INCREF(deflt); return deflt; } _PyErr_SetKeyError(key); return NULL; } // Split table doesn't allow deletion. Combine it. if (_PyDict_HasSplitTable(mp)) { if (dictresize(mp, DK_SIZE(mp->ma_keys))) { return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, &hashpos); assert(ix >= 0); } old_value = *value_addr; assert(old_value != NULL); *value_addr = NULL; mp->ma_used--; mp->ma_version_tag = DICT_NEXT_VERSION(); dk_set_index(mp->ma_keys, hashpos, DKIX_DUMMY); ep = &DK_ENTRIES(mp->ma_keys)[ix]; ENSURE_ALLOWS_DELETIONS(mp); old_key = ep->me_key; ep->me_key = NULL; Py_DECREF(old_key); assert(_PyDict_CheckConsistency(mp)); return old_value; } PyObject * _PyDict_Pop(PyObject *dict, PyObject *key, PyObject *deflt) { Py_hash_t hash; if (((PyDictObject *)dict)->ma_used == 0) { if (deflt) { Py_INCREF(deflt); return deflt; } _PyErr_SetKeyError(key); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } return _PyDict_Pop_KnownHash(dict, key, hash, deflt); } /* Internal version of dict.from_keys(). It is subclass-friendly. */ PyObject * _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) { PyObject *it; /* iter(iterable) */ PyObject *key; PyObject *d; int status; d = PyObject_CallObject(cls, NULL); if (d == NULL) return NULL; if (PyDict_CheckExact(d) && ((PyDictObject *)d)->ma_used == 0) { if (PyDict_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; PyObject *oldvalue; Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; if (dictresize(mp, ESTIMATE_SIZE(((PyDictObject *)iterable)->ma_used))) { Py_DECREF(d); return NULL; } while (_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) { if (insertdict(mp, key, hash, value)) { Py_DECREF(d); return NULL; } } return d; } if (PyAnySet_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; if (dictresize(mp, ESTIMATE_SIZE(PySet_GET_SIZE(iterable)))) { Py_DECREF(d); return NULL; } while (_PySet_NextEntry(iterable, &pos, &key, &hash)) { if (insertdict(mp, key, hash, value)) { Py_DECREF(d); return NULL; } } return d; } } it = PyObject_GetIter(iterable); if (it == NULL){ Py_DECREF(d); return NULL; } if (PyDict_CheckExact(d)) { while ((key = PyIter_Next(it)) != NULL) { status = PyDict_SetItem(d, key, value); Py_DECREF(key); if (status < 0) goto Fail; } } else { while ((key = PyIter_Next(it)) != NULL) { status = PyObject_SetItem(d, key, value); Py_DECREF(key); if (status < 0) goto Fail; } } if (PyErr_Occurred()) goto Fail; Py_DECREF(it); return d; Fail: Py_DECREF(it); Py_DECREF(d); return NULL; } /* Methods */ static void dict_dealloc(PyDictObject *mp) { PyObject **values = mp->ma_values; PyDictKeysObject *keys = mp->ma_keys; Py_ssize_t i, n; /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(mp); Py_TRASHCAN_SAFE_BEGIN(mp) if (values != NULL) { if (values != empty_values) { for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) { Py_XDECREF(values[i]); } free_values(values); } DK_DECREF(keys); } else if (keys != NULL) { assert(keys->dk_refcnt == 1); DK_DECREF(keys); } if (numfree < PyDict_MAXFREELIST && Py_TYPE(mp) == &PyDict_Type) free_list[numfree++] = mp; else Py_TYPE(mp)->tp_free((PyObject *)mp); Py_TRASHCAN_SAFE_END(mp) } static PyObject * dict_repr(PyDictObject *mp) { Py_ssize_t i; PyObject *key = NULL, *value = NULL; _PyUnicodeWriter writer; int first; i = Py_ReprEnter((PyObject *)mp); if (i != 0) { return i > 0 ? PyUnicode_FromString("{...}") : NULL; } if (mp->ma_used == 0) { Py_ReprLeave((PyObject *)mp); return PyUnicode_FromString("{}"); } _PyUnicodeWriter_Init(&writer); writer.overallocate = 1; /* "{" + "1: 2" + ", 3: 4" * (len - 1) + "}" */ writer.min_length = 1 + 4 + (2 + 4) * (mp->ma_used - 1) + 1; if (_PyUnicodeWriter_WriteChar(&writer, '{') < 0) goto error; /* Do repr() on each key+value pair, and insert ": " between them. Note that repr may mutate the dict. */ i = 0; first = 1; while (PyDict_Next((PyObject *)mp, &i, &key, &value)) { PyObject *s; int res; /* Prevent repr from deleting key or value during key format. */ Py_INCREF(key); Py_INCREF(value); if (!first) { if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0) goto error; } first = 0; s = PyObject_Repr(key); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; if (_PyUnicodeWriter_WriteASCIIString(&writer, ": ", 2) < 0) goto error; s = PyObject_Repr(value); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; Py_CLEAR(key); Py_CLEAR(value); } writer.overallocate = 0; if (_PyUnicodeWriter_WriteChar(&writer, '}') < 0) goto error; Py_ReprLeave((PyObject *)mp); return _PyUnicodeWriter_Finish(&writer); error: Py_ReprLeave((PyObject *)mp); _PyUnicodeWriter_Dealloc(&writer); Py_XDECREF(key); Py_XDECREF(value); return NULL; } static Py_ssize_t dict_length(PyDictObject *mp) { return mp->ma_used; } static PyObject * dict_subscript(PyDictObject *mp, PyObject *key) { PyObject *v; Py_ssize_t ix; Py_hash_t hash; PyObject **value_addr; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || *value_addr == NULL) { if (!PyDict_CheckExact(mp)) { /* Look up __missing__ method if we're a subclass. */ PyObject *missing, *res; _Py_IDENTIFIER(__missing__); missing = _PyObject_LookupSpecial((PyObject *)mp, &PyId___missing__); if (missing != NULL) { res = PyObject_CallFunctionObjArgs(missing, key, NULL); Py_DECREF(missing); return res; } else if (PyErr_Occurred()) return NULL; } _PyErr_SetKeyError(key); return NULL; } v = *value_addr; Py_INCREF(v); return v; } static int dict_ass_sub(PyDictObject *mp, PyObject *v, PyObject *w) { if (w == NULL) return PyDict_DelItem((PyObject *)mp, v); else return PyDict_SetItem((PyObject *)mp, v, w); } static PyMappingMethods dict_as_mapping = { (lenfunc)dict_length, /*mp_length*/ (binaryfunc)dict_subscript, /*mp_subscript*/ (objobjargproc)dict_ass_sub, /*mp_ass_subscript*/ }; static PyObject * dict_keys(PyDictObject *mp) { PyObject *v; Py_ssize_t i, j; PyDictKeyEntry *ep; Py_ssize_t size, n, offset; PyObject **value_ptr; again: n = mp->ma_used; v = PyList_New(n); if (v == NULL) return NULL; if (n != mp->ma_used) { /* Durnit. The allocations caused the dict to resize. * Just start over, this shouldn't normally happen. */ Py_DECREF(v); goto again; } ep = DK_ENTRIES(mp->ma_keys); size = mp->ma_keys->dk_nentries; if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *); } else { value_ptr = &ep[0].me_value; offset = sizeof(PyDictKeyEntry); } for (i = 0, j = 0; i < size; i++) { if (*value_ptr != NULL) { PyObject *key = ep[i].me_key; Py_INCREF(key); PyList_SET_ITEM(v, j, key); j++; } value_ptr = (PyObject **)(((char *)value_ptr) + offset); } assert(j == n); return v; } static PyObject * dict_values(PyDictObject *mp) { PyObject *v; Py_ssize_t i, j; PyDictKeyEntry *ep; Py_ssize_t size, n, offset; PyObject **value_ptr; again: n = mp->ma_used; v = PyList_New(n); if (v == NULL) return NULL; if (n != mp->ma_used) { /* Durnit. The allocations caused the dict to resize. * Just start over, this shouldn't normally happen. */ Py_DECREF(v); goto again; } ep = DK_ENTRIES(mp->ma_keys); size = mp->ma_keys->dk_nentries; if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *); } else { value_ptr = &ep[0].me_value; offset = sizeof(PyDictKeyEntry); } for (i = 0, j = 0; i < size; i++) { PyObject *value = *value_ptr; value_ptr = (PyObject **)(((char *)value_ptr) + offset); if (value != NULL) { Py_INCREF(value); PyList_SET_ITEM(v, j, value); j++; } } assert(j == n); return v; } static PyObject * dict_items(PyDictObject *mp) { PyObject *v; Py_ssize_t i, j, n; Py_ssize_t size, offset; PyObject *item, *key; PyDictKeyEntry *ep; PyObject **value_ptr; /* Preallocate the list of tuples, to avoid allocations during * the loop over the items, which could trigger GC, which * could resize the dict. :-( */ again: n = mp->ma_used; v = PyList_New(n); if (v == NULL) return NULL; for (i = 0; i < n; i++) { item = PyTuple_New(2); if (item == NULL) { Py_DECREF(v); return NULL; } PyList_SET_ITEM(v, i, item); } if (n != mp->ma_used) { /* Durnit. The allocations caused the dict to resize. * Just start over, this shouldn't normally happen. */ Py_DECREF(v); goto again; } /* Nothing we do below makes any function calls. */ ep = DK_ENTRIES(mp->ma_keys); size = mp->ma_keys->dk_nentries; if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *); } else { value_ptr = &ep[0].me_value; offset = sizeof(PyDictKeyEntry); } for (i = 0, j = 0; i < size; i++) { PyObject *value = *value_ptr; value_ptr = (PyObject **)(((char *)value_ptr) + offset); if (value != NULL) { key = ep[i].me_key; item = PyList_GET_ITEM(v, j); Py_INCREF(key); PyTuple_SET_ITEM(item, 0, key); Py_INCREF(value); PyTuple_SET_ITEM(item, 1, value); j++; } } assert(j == n); return v; } /*[clinic input] @classmethod dict.fromkeys iterable: object value: object=None / Returns a new dict with keys from iterable and values equal to value. [clinic start generated code]*/ static PyObject * dict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value) /*[clinic end generated code: output=8fb98e4b10384999 input=b85a667f9bf4669d]*/ { return _PyDict_FromKeys((PyObject *)type, iterable, value); } static int dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, const char *methname) { PyObject *arg = NULL; int result = 0; if (!PyArg_UnpackTuple(args, methname, 0, 1, &arg)) result = -1; else if (arg != NULL) { _Py_IDENTIFIER(keys); if (_PyObject_HasAttrId(arg, &PyId_keys)) result = PyDict_Merge(self, arg, 1); else result = PyDict_MergeFromSeq2(self, arg, 1); } if (result == 0 && kwds != NULL) { if (PyArg_ValidateKeywordArguments(kwds)) result = PyDict_Merge(self, kwds, 1); else result = -1; } return result; } static PyObject * dict_update(PyObject *self, PyObject *args, PyObject *kwds) { if (dict_update_common(self, args, kwds, "update") != -1) Py_RETURN_NONE; return NULL; } /* Update unconditionally replaces existing items. Merge has a 3rd argument 'override'; if set, it acts like Update, otherwise it leaves existing items unchanged. PyDict_{Update,Merge} update/merge from a mapping object. PyDict_MergeFromSeq2 updates/merges from any iterable object producing iterable objects of length 2. */ int PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override) { PyObject *it; /* iter(seq2) */ Py_ssize_t i; /* index into seq2 of current element */ PyObject *item; /* seq2[i] */ PyObject *fast; /* item as a 2-tuple or 2-list */ assert(d != NULL); assert(PyDict_Check(d)); assert(seq2 != NULL); it = PyObject_GetIter(seq2); if (it == NULL) return -1; for (i = 0; ; ++i) { PyObject *key, *value; Py_ssize_t n; fast = NULL; item = PyIter_Next(it); if (item == NULL) { if (PyErr_Occurred()) goto Fail; break; } /* Convert item to sequence, and verify length 2. */ fast = PySequence_Fast(item, ""); if (fast == NULL) { if (PyErr_ExceptionMatches(PyExc_TypeError)) PyErr_Format(PyExc_TypeError, "cannot convert dictionary update " "sequence element #%zd to a sequence", i); goto Fail; } n = PySequence_Fast_GET_SIZE(fast); if (n != 2) { PyErr_Format(PyExc_ValueError, "dictionary update sequence element #%zd " "has length %zd; 2 is required", i, n); goto Fail; } /* Update/merge with this (key, value) pair. */ key = PySequence_Fast_GET_ITEM(fast, 0); value = PySequence_Fast_GET_ITEM(fast, 1); Py_INCREF(key); Py_INCREF(value); if (override || PyDict_GetItem(d, key) == NULL) { int status = PyDict_SetItem(d, key, value); if (status < 0) { Py_DECREF(key); Py_DECREF(value); goto Fail; } } Py_DECREF(key); Py_DECREF(value); Py_DECREF(fast); Py_DECREF(item); } i = 0; assert(_PyDict_CheckConsistency((PyDictObject *)d)); goto Return; Fail: Py_XDECREF(item); Py_XDECREF(fast); i = -1; Return: Py_DECREF(it); return Py_SAFE_DOWNCAST(i, Py_ssize_t, int); } static int dict_merge(PyObject *a, PyObject *b, int override) { PyDictObject *mp, *other; Py_ssize_t i, n; PyDictKeyEntry *entry, *ep0; assert(0 <= override && override <= 2); /* We accept for the argument either a concrete dictionary object, * or an abstract "mapping" object. For the former, we can do * things quite efficiently. For the latter, we only require that * PyMapping_Keys() and PyObject_GetItem() be supported. */ if (a == NULL || !PyDict_Check(a) || b == NULL) { PyErr_BadInternalCall(); return -1; } mp = (PyDictObject*)a; if (PyDict_Check(b) && (Py_TYPE(b)->tp_iter == (getiterfunc)dict_iter)) { other = (PyDictObject*)b; if (other == mp || other->ma_used == 0) /* a.update(a) or a.update({}); nothing to do */ return 0; if (mp->ma_used == 0) /* Since the target dict is empty, PyDict_GetItem() * always returns NULL. Setting override to 1 * skips the unnecessary test. */ override = 1; /* Do one big resize at the start, rather than * incrementally resizing as we insert new items. Expect * that there will be no (or few) overlapping keys. */ if (USABLE_FRACTION(mp->ma_keys->dk_size) < other->ma_used) { if (dictresize(mp, ESTIMATE_SIZE(mp->ma_used + other->ma_used))) { return -1; } } ep0 = DK_ENTRIES(other->ma_keys); for (i = 0, n = other->ma_keys->dk_nentries; i < n; i++) { PyObject *key, *value; Py_hash_t hash; entry = &ep0[i]; key = entry->me_key; hash = entry->me_hash; if (other->ma_values) value = other->ma_values[i]; else value = entry->me_value; if (value != NULL) { int err = 0; Py_INCREF(key); Py_INCREF(value); if (override == 1) err = insertdict(mp, key, hash, value); else if (_PyDict_GetItem_KnownHash(a, key, hash) == NULL) { if (PyErr_Occurred()) { Py_DECREF(value); Py_DECREF(key); return -1; } err = insertdict(mp, key, hash, value); } else if (override != 0) { _PyErr_SetKeyError(key); Py_DECREF(value); Py_DECREF(key); return -1; } Py_DECREF(value); Py_DECREF(key); if (err != 0) return -1; if (n != other->ma_keys->dk_nentries) { PyErr_SetString(PyExc_RuntimeError, "dict mutated during update"); return -1; } } } } else { /* Do it the generic, slower way */ PyObject *keys = PyMapping_Keys(b); PyObject *iter; PyObject *key, *value; int status; if (keys == NULL) /* Docstring says this is equivalent to E.keys() so * if E doesn't have a .keys() method we want * AttributeError to percolate up. Might as well * do the same for any other error. */ return -1; iter = PyObject_GetIter(keys); Py_DECREF(keys); if (iter == NULL) return -1; for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) { if (override != 1 && PyDict_GetItem(a, key) != NULL) { if (override != 0) { _PyErr_SetKeyError(key); Py_DECREF(key); Py_DECREF(iter); return -1; } Py_DECREF(key); continue; } value = PyObject_GetItem(b, key); if (value == NULL) { Py_DECREF(iter); Py_DECREF(key); return -1; } status = PyDict_SetItem(a, key, value); Py_DECREF(key); Py_DECREF(value); if (status < 0) { Py_DECREF(iter); return -1; } } Py_DECREF(iter); if (PyErr_Occurred()) /* Iterator completed, via error */ return -1; } assert(_PyDict_CheckConsistency((PyDictObject *)a)); return 0; } int PyDict_Update(PyObject *a, PyObject *b) { return dict_merge(a, b, 1); } int PyDict_Merge(PyObject *a, PyObject *b, int override) { /* XXX Deprecate override not in (0, 1). */ return dict_merge(a, b, override != 0); } int _PyDict_MergeEx(PyObject *a, PyObject *b, int override) { return dict_merge(a, b, override); } static PyObject * dict_copy(PyDictObject *mp) { return PyDict_Copy((PyObject*)mp); } PyObject * PyDict_Copy(PyObject *o) { PyObject *copy; PyDictObject *mp; Py_ssize_t i, n; if (o == NULL || !PyDict_Check(o)) { PyErr_BadInternalCall(); return NULL; } mp = (PyDictObject *)o; if (_PyDict_HasSplitTable(mp)) { PyDictObject *split_copy; Py_ssize_t size = USABLE_FRACTION(DK_SIZE(mp->ma_keys)); PyObject **newvalues; newvalues = new_values(size); if (newvalues == NULL) return PyErr_NoMemory(); split_copy = PyObject_GC_New(PyDictObject, &PyDict_Type); if (split_copy == NULL) { free_values(newvalues); return NULL; } split_copy->ma_values = newvalues; split_copy->ma_keys = mp->ma_keys; split_copy->ma_used = mp->ma_used; split_copy->ma_version_tag = DICT_NEXT_VERSION(); DK_INCREF(mp->ma_keys); for (i = 0, n = size; i < n; i++) { PyObject *value = mp->ma_values[i]; Py_XINCREF(value); split_copy->ma_values[i] = value; } if (_PyObject_GC_IS_TRACKED(mp)) _PyObject_GC_TRACK(split_copy); return (PyObject *)split_copy; } copy = PyDict_New(); if (copy == NULL) return NULL; if (PyDict_Merge(copy, o, 1) == 0) return copy; Py_DECREF(copy); return NULL; } Py_ssize_t PyDict_Size(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return -1; } return ((PyDictObject *)mp)->ma_used; } PyObject * PyDict_Keys(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return NULL; } return dict_keys((PyDictObject *)mp); } PyObject * PyDict_Values(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return NULL; } return dict_values((PyDictObject *)mp); } PyObject * PyDict_Items(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return NULL; } return dict_items((PyDictObject *)mp); } /* Return 1 if dicts equal, 0 if not, -1 if error. * Gets out as soon as any difference is detected. * Uses only Py_EQ comparison. */ static int dict_equal(PyDictObject *a, PyDictObject *b) { Py_ssize_t i; if (a->ma_used != b->ma_used) /* can't be equal if # of entries differ */ return 0; /* Same # of entries -- check all of 'em. Exit early on any diff. */ for (i = 0; i < a->ma_keys->dk_nentries; i++) { PyDictKeyEntry *ep = &DK_ENTRIES(a->ma_keys)[i]; PyObject *aval; if (a->ma_values) aval = a->ma_values[i]; else aval = ep->me_value; if (aval != NULL) { int cmp; PyObject *bval; PyObject **vaddr; PyObject *key = ep->me_key; /* temporarily bump aval's refcount to ensure it stays alive until we're done with it */ Py_INCREF(aval); /* ditto for key */ Py_INCREF(key); /* reuse the known hash value */ if ((b->ma_keys->dk_lookup)(b, key, ep->me_hash, &vaddr, NULL) < 0) bval = NULL; else bval = *vaddr; if (bval == NULL) { Py_DECREF(key); Py_DECREF(aval); if (PyErr_Occurred()) return -1; return 0; } cmp = PyObject_RichCompareBool(aval, bval, Py_EQ); Py_DECREF(key); Py_DECREF(aval); if (cmp <= 0) /* error or not equal */ return cmp; } } return 1; } static PyObject * dict_richcompare(PyObject *v, PyObject *w, int op) { int cmp; PyObject *res; if (!PyDict_Check(v) || !PyDict_Check(w)) { res = Py_NotImplemented; } else if (op == Py_EQ || op == Py_NE) { cmp = dict_equal((PyDictObject *)v, (PyDictObject *)w); if (cmp < 0) return NULL; res = (cmp == (op == Py_EQ)) ? Py_True : Py_False; } else res = Py_NotImplemented; Py_INCREF(res); return res; } /*[clinic input] @coexist dict.__contains__ key: object / True if D has a key k, else False. [clinic start generated code]*/ static PyObject * dict___contains__(PyDictObject *self, PyObject *key) /*[clinic end generated code: output=a3d03db709ed6e6b input=b852b2a19b51ab24]*/ { register PyDictObject *mp = self; Py_hash_t hash; Py_ssize_t ix; PyObject **value_addr; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || *value_addr == NULL) Py_RETURN_FALSE; Py_RETURN_TRUE; } static PyObject * dict_get(PyDictObject *mp, PyObject *args) { PyObject *key; PyObject *failobj = Py_None; PyObject *val = NULL; Py_hash_t hash; Py_ssize_t ix; PyObject **value_addr; if (!PyArg_UnpackTuple(args, "get", 1, 2, &key, &failobj)) return NULL; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || *value_addr == NULL) val = failobj; else val = *value_addr; Py_INCREF(val); return val; } PyObject * PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj) { PyDictObject *mp = (PyDictObject *)d; PyObject *value; Py_hash_t hash; Py_ssize_t hashpos, ix; PyObject **value_addr; if (!PyDict_Check(d)) { PyErr_BadInternalCall(); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) { if (insertion_resize(mp) < 0) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, &hashpos); if (ix == DKIX_ERROR) return NULL; if (_PyDict_HasSplitTable(mp) && ((ix >= 0 && *value_addr == NULL && mp->ma_used != ix) || (ix == DKIX_EMPTY && mp->ma_used != mp->ma_keys->dk_nentries))) { if (insertion_resize(mp) < 0) { return NULL; } find_empty_slot(mp, key, hash, &value_addr, &hashpos); ix = DKIX_EMPTY; } if (ix == DKIX_EMPTY) { PyDictKeyEntry *ep, *ep0; value = defaultobj; if (mp->ma_keys->dk_usable <= 0) { if (insertion_resize(mp) < 0) { return NULL; } find_empty_slot(mp, key, hash, &value_addr, &hashpos); } ep0 = DK_ENTRIES(mp->ma_keys); ep = &ep0[mp->ma_keys->dk_nentries]; dk_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries); Py_INCREF(key); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); ep->me_key = key; ep->me_hash = hash; if (mp->ma_values) { assert(mp->ma_values[mp->ma_keys->dk_nentries] == NULL); mp->ma_values[mp->ma_keys->dk_nentries] = value; } else { ep->me_value = value; } mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); mp->ma_keys->dk_usable--; mp->ma_keys->dk_nentries++; assert(mp->ma_keys->dk_usable >= 0); } else if (*value_addr == NULL) { value = defaultobj; assert(_PyDict_HasSplitTable(mp)); assert(ix == mp->ma_used); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); *value_addr = value; mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); } else { value = *value_addr; } assert(_PyDict_CheckConsistency(mp)); return value; } static PyObject * dict_setdefault(PyDictObject *mp, PyObject *args) { PyObject *key, *val; PyObject *defaultobj = Py_None; if (!PyArg_UnpackTuple(args, "setdefault", 1, 2, &key, &defaultobj)) return NULL; val = PyDict_SetDefault((PyObject *)mp, key, defaultobj); Py_XINCREF(val); return val; } static PyObject * dict_clear(PyDictObject *mp) { PyDict_Clear((PyObject *)mp); Py_RETURN_NONE; } static PyObject * dict_pop(PyDictObject *mp, PyObject *args) { PyObject *key, *deflt = NULL; if(!PyArg_UnpackTuple(args, "pop", 1, 2, &key, &deflt)) return NULL; return _PyDict_Pop((PyObject*)mp, key, deflt); } static PyObject * dict_popitem(PyDictObject *mp) { Py_ssize_t i, j; PyDictKeyEntry *ep0, *ep; PyObject *res; /* Allocate the result tuple before checking the size. Believe it * or not, this allocation could trigger a garbage collection which * could empty the dict, so if we checked the size first and that * happened, the result would be an infinite loop (searching for an * entry that no longer exists). Note that the usual popitem() * idiom is "while d: k, v = d.popitem()". so needing to throw the * tuple away if the dict *is* empty isn't a significant * inefficiency -- possible, but unlikely in practice. */ res = PyTuple_New(2); if (res == NULL) return NULL; if (mp->ma_used == 0) { Py_DECREF(res); PyErr_SetString(PyExc_KeyError, "popitem(): dictionary is empty"); return NULL; } /* Convert split table to combined table */ if (mp->ma_keys->dk_lookup == lookdict_split) { if (dictresize(mp, DK_SIZE(mp->ma_keys))) { Py_DECREF(res); return NULL; } } ENSURE_ALLOWS_DELETIONS(mp); /* Pop last item */ ep0 = DK_ENTRIES(mp->ma_keys); i = mp->ma_keys->dk_nentries - 1; while (i >= 0 && ep0[i].me_value == NULL) { i--; } assert(i >= 0); ep = &ep0[i]; j = lookdict_index(mp->ma_keys, ep->me_hash, i); assert(j >= 0); assert(dk_get_index(mp->ma_keys, j) == i); dk_set_index(mp->ma_keys, j, DKIX_DUMMY); PyTuple_SET_ITEM(res, 0, ep->me_key); PyTuple_SET_ITEM(res, 1, ep->me_value); ep->me_key = NULL; ep->me_value = NULL; /* We can't dk_usable++ since there is DKIX_DUMMY in indices */ mp->ma_keys->dk_nentries = i; mp->ma_used--; mp->ma_version_tag = DICT_NEXT_VERSION(); assert(_PyDict_CheckConsistency(mp)); return res; } static int dict_traverse(PyObject *op, visitproc visit, void *arg) { PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n = keys->dk_nentries; if (keys->dk_lookup == lookdict) { for (i = 0; i < n; i++) { if (entries[i].me_value != NULL) { Py_VISIT(entries[i].me_value); Py_VISIT(entries[i].me_key); } } } else { if (mp->ma_values != NULL) { for (i = 0; i < n; i++) { Py_VISIT(mp->ma_values[i]); } } else { for (i = 0; i < n; i++) { Py_VISIT(entries[i].me_value); } } } return 0; } static int dict_tp_clear(PyObject *op) { PyDict_Clear(op); return 0; } static PyObject *dictiter_new(PyDictObject *, PyTypeObject *); Py_ssize_t _PyDict_SizeOf(PyDictObject *mp) { Py_ssize_t size, usable, res; size = DK_SIZE(mp->ma_keys); usable = USABLE_FRACTION(size); res = _PyObject_SIZE(Py_TYPE(mp)); if (mp->ma_values) res += usable * sizeof(PyObject*); /* If the dictionary is split, the keys portion is accounted-for in the type object. */ if (mp->ma_keys->dk_refcnt == 1) res += (sizeof(PyDictKeysObject) + DK_IXSIZE(mp->ma_keys) * size + sizeof(PyDictKeyEntry) * usable); return res; } Py_ssize_t _PyDict_KeysSize(PyDictKeysObject *keys) { return (sizeof(PyDictKeysObject) + DK_IXSIZE(keys) * DK_SIZE(keys) + USABLE_FRACTION(DK_SIZE(keys)) * sizeof(PyDictKeyEntry)); } static PyObject * dict_sizeof(PyDictObject *mp) { return PyLong_FromSsize_t(_PyDict_SizeOf(mp)); } PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]"); PyDoc_STRVAR(sizeof__doc__, "D.__sizeof__() -> size of D in memory, in bytes"); PyDoc_STRVAR(get__doc__, "D.get(k[,d]) -> D[k] if k in D, else d. d defaults to None."); PyDoc_STRVAR(setdefault_doc__, "D.setdefault(k[,d]) -> D.get(k,d), also set D[k]=d if k not in D"); PyDoc_STRVAR(pop__doc__, "D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\n\ If key is not found, d is returned if given, otherwise KeyError is raised"); PyDoc_STRVAR(popitem__doc__, "D.popitem() -> (k, v), remove and return some (key, value) pair as a\n\ 2-tuple; but raise KeyError if D is empty."); PyDoc_STRVAR(update__doc__, "D.update([E, ]**F) -> None. Update D from dict/iterable E and F.\n\ If E is present and has a .keys() method, then does: for k in E: D[k] = E[k]\n\ If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v\n\ In either case, this is followed by: for k in F: D[k] = F[k]"); PyDoc_STRVAR(clear__doc__, "D.clear() -> None. Remove all items from D."); PyDoc_STRVAR(copy__doc__, "D.copy() -> a shallow copy of D"); /* Forward */ static PyObject *dictkeys_new(PyObject *); static PyObject *dictitems_new(PyObject *); static PyObject *dictvalues_new(PyObject *); PyDoc_STRVAR(keys__doc__, "D.keys() -> a set-like object providing a view on D's keys"); PyDoc_STRVAR(items__doc__, "D.items() -> a set-like object providing a view on D's items"); PyDoc_STRVAR(values__doc__, "D.values() -> an object providing a view on D's values"); static PyMethodDef mapp_methods[] = { DICT___CONTAINS___METHODDEF {"__getitem__", (PyCFunction)dict_subscript, METH_O | METH_COEXIST, getitem__doc__}, {"__sizeof__", (PyCFunction)dict_sizeof, METH_NOARGS, sizeof__doc__}, {"get", (PyCFunction)dict_get, METH_VARARGS, get__doc__}, {"setdefault", (PyCFunction)dict_setdefault, METH_VARARGS, setdefault_doc__}, {"pop", (PyCFunction)dict_pop, METH_VARARGS, pop__doc__}, {"popitem", (PyCFunction)dict_popitem, METH_NOARGS, popitem__doc__}, {"keys", (PyCFunction)dictkeys_new, METH_NOARGS, keys__doc__}, {"items", (PyCFunction)dictitems_new, METH_NOARGS, items__doc__}, {"values", (PyCFunction)dictvalues_new, METH_NOARGS, values__doc__}, {"update", (PyCFunction)dict_update, METH_VARARGS | METH_KEYWORDS, update__doc__}, DICT_FROMKEYS_METHODDEF {"clear", (PyCFunction)dict_clear, METH_NOARGS, clear__doc__}, {"copy", (PyCFunction)dict_copy, METH_NOARGS, copy__doc__}, {NULL, NULL} /* sentinel */ }; /* Return 1 if `key` is in dict `op`, 0 if not, and -1 on error. */ int PyDict_Contains(PyObject *op, PyObject *key) { Py_hash_t hash; Py_ssize_t ix; PyDictObject *mp = (PyDictObject *)op; PyObject **value_addr; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL); if (ix == DKIX_ERROR) return -1; return (ix != DKIX_EMPTY && *value_addr != NULL); } /* Internal version of PyDict_Contains used when the hash value is already known */ int _PyDict_Contains(PyObject *op, PyObject *key, Py_hash_t hash) { PyDictObject *mp = (PyDictObject *)op; PyObject **value_addr; Py_ssize_t ix; ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value_addr, NULL); if (ix == DKIX_ERROR) return -1; return (ix != DKIX_EMPTY && *value_addr != NULL); } /* Hack to implement "key in dict" */ static PySequenceMethods dict_as_sequence = { 0, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ PyDict_Contains, /* sq_contains */ 0, /* sq_inplace_concat */ 0, /* sq_inplace_repeat */ }; static PyObject * dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *self; PyDictObject *d; assert(type != NULL && type->tp_alloc != NULL); self = type->tp_alloc(type, 0); if (self == NULL) return NULL; d = (PyDictObject *)self; /* The object has been implicitly tracked by tp_alloc */ if (type == &PyDict_Type) _PyObject_GC_UNTRACK(d); d->ma_used = 0; d->ma_version_tag = DICT_NEXT_VERSION(); d->ma_keys = new_keys_object(PyDict_MINSIZE); if (d->ma_keys == NULL) { Py_DECREF(self); return NULL; } assert(_PyDict_CheckConsistency(d)); return self; } static int dict_init(PyObject *self, PyObject *args, PyObject *kwds) { return dict_update_common(self, args, kwds, "dict"); } static PyObject * dict_iter(PyDictObject *dict) { return dictiter_new(dict, &PyDictIterKey_Type); } PyDoc_STRVAR(dictionary_doc, "dict() -> new empty dictionary\n" "dict(mapping) -> new dictionary initialized from a mapping object's\n" " (key, value) pairs\n" "dict(iterable) -> new dictionary initialized as if via:\n" " d = {}\n" " for k, v in iterable:\n" " d[k] = v\n" "dict(**kwargs) -> new dictionary initialized with the name=value pairs\n" " in the keyword argument list. For example: dict(one=1, two=2)"); PyTypeObject PyDict_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict", sizeof(PyDictObject), 0, (destructor)dict_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ (reprfunc)dict_repr, /* tp_repr */ 0, /* tp_as_number */ &dict_as_sequence, /* tp_as_sequence */ &dict_as_mapping, /* tp_as_mapping */ PyObject_HashNotImplemented, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_DICT_SUBCLASS, /* tp_flags */ dictionary_doc, /* tp_doc */ dict_traverse, /* tp_traverse */ dict_tp_clear, /* tp_clear */ dict_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dict_iter, /* tp_iter */ 0, /* tp_iternext */ mapp_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ dict_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ dict_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ }; PyObject * _PyDict_GetItemId(PyObject *dp, struct _Py_Identifier *key) { PyObject *kv; kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) { PyErr_Clear(); return NULL; } return PyDict_GetItem(dp, kv); } /* For backward compatibility with old dictionary interface */ PyObject * PyDict_GetItemString(PyObject *v, const char *key) { PyObject *kv, *rv; kv = PyUnicode_FromString(key); if (kv == NULL) { PyErr_Clear(); return NULL; } rv = PyDict_GetItem(v, kv); Py_DECREF(kv); return rv; } int _PyDict_SetItemId(PyObject *v, struct _Py_Identifier *key, PyObject *item) { PyObject *kv; kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) return -1; return PyDict_SetItem(v, kv, item); } int PyDict_SetItemString(PyObject *v, const char *key, PyObject *item) { PyObject *kv; int err; kv = PyUnicode_FromString(key); if (kv == NULL) return -1; PyUnicode_InternInPlace(&kv); /* XXX Should we really? */ err = PyDict_SetItem(v, kv, item); Py_DECREF(kv); return err; } int _PyDict_DelItemId(PyObject *v, _Py_Identifier *key) { PyObject *kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) return -1; return PyDict_DelItem(v, kv); } int PyDict_DelItemString(PyObject *v, const char *key) { PyObject *kv; int err; kv = PyUnicode_FromString(key); if (kv == NULL) return -1; err = PyDict_DelItem(v, kv); Py_DECREF(kv); return err; } /* Dictionary iterator types */ typedef struct { PyObject_HEAD PyDictObject *di_dict; /* Set to NULL when iterator is exhausted */ Py_ssize_t di_used; Py_ssize_t di_pos; PyObject* di_result; /* reusable result tuple for iteritems */ Py_ssize_t len; } dictiterobject; static PyObject * dictiter_new(PyDictObject *dict, PyTypeObject *itertype) { dictiterobject *di; di = PyObject_GC_New(dictiterobject, itertype); if (di == NULL) return NULL; Py_INCREF(dict); di->di_dict = dict; di->di_used = dict->ma_used; di->di_pos = 0; di->len = dict->ma_used; if (itertype == &PyDictIterItem_Type) { di->di_result = PyTuple_Pack(2, Py_None, Py_None); if (di->di_result == NULL) { Py_DECREF(di); return NULL; } } else di->di_result = NULL; _PyObject_GC_TRACK(di); return (PyObject *)di; } static void dictiter_dealloc(dictiterobject *di) { /* bpo-31095: UnTrack is needed before calling any callbacks */ _PyObject_GC_UNTRACK(di); Py_XDECREF(di->di_dict); Py_XDECREF(di->di_result); PyObject_GC_Del(di); } static int dictiter_traverse(dictiterobject *di, visitproc visit, void *arg) { Py_VISIT(di->di_dict); Py_VISIT(di->di_result); return 0; } static PyObject * dictiter_len(dictiterobject *di) { Py_ssize_t len = 0; if (di->di_dict != NULL && di->di_used == di->di_dict->ma_used) len = di->len; return PyLong_FromSize_t(len); } PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); static PyObject * dictiter_reduce(dictiterobject *di); PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyMethodDef dictiter_methods[] = { {"__length_hint__", (PyCFunction)dictiter_len, METH_NOARGS, length_hint_doc}, {"__reduce__", (PyCFunction)dictiter_reduce, METH_NOARGS, reduce_doc}, {NULL, NULL} /* sentinel */ }; static PyObject* dictiter_iternextkey(dictiterobject *di) { PyObject *key; Py_ssize_t i, n; PyDictKeysObject *k; PyDictObject *d = di->di_dict; if (d == NULL) return NULL; assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } i = di->di_pos; k = d->ma_keys; n = k->dk_nentries; if (d->ma_values) { PyObject **value_ptr = &d->ma_values[i]; while (i < n && *value_ptr == NULL) { value_ptr++; i++; } if (i >= n) goto fail; key = DK_ENTRIES(k)[i].me_key; } else { PyDictKeyEntry *entry_ptr = &DK_ENTRIES(k)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) goto fail; key = entry_ptr->me_key; } di->di_pos = i+1; di->len--; Py_INCREF(key); return key; fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictIterKey_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_keyiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)dictiter_iternextkey, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject * dictiter_iternextvalue(dictiterobject *di) { PyObject *value; Py_ssize_t i, n; PyDictObject *d = di->di_dict; if (d == NULL) return NULL; assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } i = di->di_pos; n = d->ma_keys->dk_nentries; if (d->ma_values) { PyObject **value_ptr = &d->ma_values[i]; while (i < n && *value_ptr == NULL) { value_ptr++; i++; } if (i >= n) goto fail; value = *value_ptr; } else { PyDictKeyEntry *entry_ptr = &DK_ENTRIES(d->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) goto fail; value = entry_ptr->me_value; } di->di_pos = i+1; di->len--; Py_INCREF(value); return value; fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictIterValue_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_valueiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)dictiter_iternextvalue, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject * dictiter_iternextitem(dictiterobject *di) { PyObject *key, *value, *result; Py_ssize_t i, n; PyDictObject *d = di->di_dict; if (d == NULL) return NULL; assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } i = di->di_pos; n = d->ma_keys->dk_nentries; if (d->ma_values) { PyObject **value_ptr = &d->ma_values[i]; while (i < n && *value_ptr == NULL) { value_ptr++; i++; } if (i >= n) goto fail; key = DK_ENTRIES(d->ma_keys)[i].me_key; value = *value_ptr; } else { PyDictKeyEntry *entry_ptr = &DK_ENTRIES(d->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) goto fail; key = entry_ptr->me_key; value = entry_ptr->me_value; } di->di_pos = i+1; di->len--; Py_INCREF(key); Py_INCREF(value); result = di->di_result; if (Py_REFCNT(result) == 1) { PyObject *oldkey = PyTuple_GET_ITEM(result, 0); PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); } else { result = PyTuple_New(2); if (result == NULL) return NULL; PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ } return result; fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictIterItem_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_itemiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)dictiter_iternextitem, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject * dictiter_reduce(dictiterobject *di) { PyObject *list; dictiterobject tmp; list = PyList_New(0); if (!list) return NULL; /* copy the itertor state */ tmp = *di; Py_XINCREF(tmp.di_dict); /* iterate the temporary into a list */ for(;;) { PyObject *element = 0; if (Py_TYPE(di) == &PyDictIterItem_Type) element = dictiter_iternextitem(&tmp); else if (Py_TYPE(di) == &PyDictIterKey_Type) element = dictiter_iternextkey(&tmp); else if (Py_TYPE(di) == &PyDictIterValue_Type) element = dictiter_iternextvalue(&tmp); else assert(0); if (element) { if (PyList_Append(list, element)) { Py_DECREF(element); Py_DECREF(list); Py_XDECREF(tmp.di_dict); return NULL; } Py_DECREF(element); } else break; } Py_XDECREF(tmp.di_dict); /* check for error */ if (tmp.di_dict != NULL) { /* we have an error */ Py_DECREF(list); return NULL; } return Py_BuildValue("N(N)", _PyObject_GetBuiltin("iter"), list); } /***********************************************/ /* View objects for keys(), items(), values(). */ /***********************************************/ /* The instance lay-out is the same for all three; but the type differs. */ static void dictview_dealloc(_PyDictViewObject *dv) { /* bpo-31095: UnTrack is needed before calling any callbacks */ _PyObject_GC_UNTRACK(dv); Py_XDECREF(dv->dv_dict); PyObject_GC_Del(dv); } static int dictview_traverse(_PyDictViewObject *dv, visitproc visit, void *arg) { Py_VISIT(dv->dv_dict); return 0; } static Py_ssize_t dictview_len(_PyDictViewObject *dv) { Py_ssize_t len = 0; if (dv->dv_dict != NULL) len = dv->dv_dict->ma_used; return len; } PyObject * _PyDictView_New(PyObject *dict, PyTypeObject *type) { _PyDictViewObject *dv; if (dict == NULL) { PyErr_BadInternalCall(); return NULL; } if (!PyDict_Check(dict)) { /* XXX Get rid of this restriction later */ PyErr_Format(PyExc_TypeError, "%s() requires a dict argument, not '%s'", type->tp_name, dict->ob_type->tp_name); return NULL; } dv = PyObject_GC_New(_PyDictViewObject, type); if (dv == NULL) return NULL; Py_INCREF(dict); dv->dv_dict = (PyDictObject *)dict; _PyObject_GC_TRACK(dv); return (PyObject *)dv; } /* TODO(guido): The views objects are not complete: * support more set operations * support arbitrary mappings? - either these should be static or exported in dictobject.h - if public then they should probably be in builtins */ /* Return 1 if self is a subset of other, iterating over self; 0 if not; -1 if an error occurred. */ static int all_contained_in(PyObject *self, PyObject *other) { PyObject *iter = PyObject_GetIter(self); int ok = 1; if (iter == NULL) return -1; for (;;) { PyObject *next = PyIter_Next(iter); if (next == NULL) { if (PyErr_Occurred()) ok = -1; break; } ok = PySequence_Contains(other, next); Py_DECREF(next); if (ok <= 0) break; } Py_DECREF(iter); return ok; } static PyObject * dictview_richcompare(PyObject *self, PyObject *other, int op) { Py_ssize_t len_self, len_other; int ok; PyObject *result; assert(self != NULL); assert(PyDictViewSet_Check(self)); assert(other != NULL); if (!PyAnySet_Check(other) && !PyDictViewSet_Check(other)) Py_RETURN_NOTIMPLEMENTED; len_self = PyObject_Size(self); if (len_self < 0) return NULL; len_other = PyObject_Size(other); if (len_other < 0) return NULL; ok = 0; switch(op) { case Py_NE: case Py_EQ: if (len_self == len_other) ok = all_contained_in(self, other); if (op == Py_NE && ok >= 0) ok = !ok; break; case Py_LT: if (len_self < len_other) ok = all_contained_in(self, other); break; case Py_LE: if (len_self <= len_other) ok = all_contained_in(self, other); break; case Py_GT: if (len_self > len_other) ok = all_contained_in(other, self); break; case Py_GE: if (len_self >= len_other) ok = all_contained_in(other, self); break; } if (ok < 0) return NULL; result = ok ? Py_True : Py_False; Py_INCREF(result); return result; } static PyObject * dictview_repr(_PyDictViewObject *dv) { PyObject *seq; PyObject *result = NULL; Py_ssize_t rc; rc = Py_ReprEnter((PyObject *)dv); if (rc != 0) { return rc > 0 ? PyUnicode_FromString("...") : NULL; } seq = PySequence_List((PyObject *)dv); if (seq == NULL) { goto Done; } result = PyUnicode_FromFormat("%s(%R)", Py_TYPE(dv)->tp_name, seq); Py_DECREF(seq); Done: Py_ReprLeave((PyObject *)dv); return result; } /*** dict_keys ***/ static PyObject * dictkeys_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictIterKey_Type); } static int dictkeys_contains(_PyDictViewObject *dv, PyObject *obj) { if (dv->dv_dict == NULL) return 0; return PyDict_Contains((PyObject *)dv->dv_dict, obj); } static PySequenceMethods dictkeys_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictkeys_contains, /* sq_contains */ }; static PyObject* dictviews_sub(PyObject* self, PyObject *other) { PyObject *result = PySet_New(self); PyObject *tmp; _Py_IDENTIFIER(difference_update); if (result == NULL) return NULL; tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_difference_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } PyObject* _PyDictView_Intersect(PyObject* self, PyObject *other) { PyObject *result = PySet_New(self); PyObject *tmp; _Py_IDENTIFIER(intersection_update); if (result == NULL) return NULL; tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_intersection_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static PyObject* dictviews_or(PyObject* self, PyObject *other) { PyObject *result = PySet_New(self); PyObject *tmp; _Py_IDENTIFIER(update); if (result == NULL) return NULL; tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static PyObject* dictviews_xor(PyObject* self, PyObject *other) { PyObject *result = PySet_New(self); PyObject *tmp; _Py_IDENTIFIER(symmetric_difference_update); if (result == NULL) return NULL; tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_symmetric_difference_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static PyNumberMethods dictviews_as_number = { 0, /*nb_add*/ (binaryfunc)dictviews_sub, /*nb_subtract*/ 0, /*nb_multiply*/ 0, /*nb_remainder*/ 0, /*nb_divmod*/ 0, /*nb_power*/ 0, /*nb_negative*/ 0, /*nb_positive*/ 0, /*nb_absolute*/ 0, /*nb_bool*/ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ (binaryfunc)_PyDictView_Intersect, /*nb_and*/ (binaryfunc)dictviews_xor, /*nb_xor*/ (binaryfunc)dictviews_or, /*nb_or*/ }; static PyObject* dictviews_isdisjoint(PyObject *self, PyObject *other) { PyObject *it; PyObject *item = NULL; if (self == other) { if (dictview_len((_PyDictViewObject *)self) == 0) Py_RETURN_TRUE; else Py_RETURN_FALSE; } /* Iterate over the shorter object (only if other is a set, * because PySequence_Contains may be expensive otherwise): */ if (PyAnySet_Check(other) || PyDictViewSet_Check(other)) { Py_ssize_t len_self = dictview_len((_PyDictViewObject *)self); Py_ssize_t len_other = PyObject_Size(other); if (len_other == -1) return NULL; if ((len_other > len_self)) { PyObject *tmp = other; other = self; self = tmp; } } it = PyObject_GetIter(other); if (it == NULL) return NULL; while ((item = PyIter_Next(it)) != NULL) { int contains = PySequence_Contains(self, item); Py_DECREF(item); if (contains == -1) { Py_DECREF(it); return NULL; } if (contains) { Py_DECREF(it); Py_RETURN_FALSE; } } Py_DECREF(it); if (PyErr_Occurred()) return NULL; /* PyIter_Next raised an exception. */ Py_RETURN_TRUE; } PyDoc_STRVAR(isdisjoint_doc, "Return True if the view and the given iterable have a null intersection."); static PyMethodDef dictkeys_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictKeys_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_keys", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictkeys_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dictkeys_iter, /* tp_iter */ 0, /* tp_iternext */ dictkeys_methods, /* tp_methods */ 0, }; static PyObject * dictkeys_new(PyObject *dict) { return _PyDictView_New(dict, &PyDictKeys_Type); } /*** dict_items ***/ static PyObject * dictitems_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictIterItem_Type); } static int dictitems_contains(_PyDictViewObject *dv, PyObject *obj) { int result; PyObject *key, *value, *found; if (dv->dv_dict == NULL) return 0; if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) return 0; key = PyTuple_GET_ITEM(obj, 0); value = PyTuple_GET_ITEM(obj, 1); found = PyDict_GetItemWithError((PyObject *)dv->dv_dict, key); if (found == NULL) { if (PyErr_Occurred()) return -1; return 0; } Py_INCREF(found); result = PyObject_RichCompareBool(value, found, Py_EQ); Py_DECREF(found); return result; } static PySequenceMethods dictitems_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictitems_contains, /* sq_contains */ }; static PyMethodDef dictitems_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictItems_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_items", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictitems_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dictitems_iter, /* tp_iter */ 0, /* tp_iternext */ dictitems_methods, /* tp_methods */ 0, }; static PyObject * dictitems_new(PyObject *dict) { return _PyDictView_New(dict, &PyDictItems_Type); } /*** dict_values ***/ static PyObject * dictvalues_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictIterValue_Type); } static PySequenceMethods dictvalues_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)0, /* sq_contains */ }; static PyMethodDef dictvalues_methods[] = { {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictValues_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_values", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ (reprfunc)dictview_repr, /* tp_repr */ 0, /* tp_as_number */ &dictvalues_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dictvalues_iter, /* tp_iter */ 0, /* tp_iternext */ dictvalues_methods, /* tp_methods */ 0, }; static PyObject * dictvalues_new(PyObject *dict) { return _PyDictView_New(dict, &PyDictValues_Type); } /* Returns NULL if cannot allocate a new PyDictKeysObject, but does not set an error */ PyDictKeysObject * _PyDict_NewKeysForClass(void) { PyDictKeysObject *keys = new_keys_object(PyDict_MINSIZE); if (keys == NULL) PyErr_Clear(); else keys->dk_lookup = lookdict_split; return keys; } #define CACHED_KEYS(tp) (((PyHeapTypeObject*)tp)->ht_cached_keys) PyObject * PyObject_GenericGetDict(PyObject *obj, void *context) { PyObject *dict, **dictptr = _PyObject_GetDictPtr(obj); if (dictptr == NULL) { PyErr_SetString(PyExc_AttributeError, "This object has no __dict__"); return NULL; } dict = *dictptr; if (dict == NULL) { PyTypeObject *tp = Py_TYPE(obj); if ((tp->tp_flags & Py_TPFLAGS_HEAPTYPE) && CACHED_KEYS(tp)) { DK_INCREF(CACHED_KEYS(tp)); *dictptr = dict = new_dict_with_shared_keys(CACHED_KEYS(tp)); } else { *dictptr = dict = PyDict_New(); } } Py_XINCREF(dict); return dict; } int _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, PyObject *key, PyObject *value) { PyObject *dict; int res; PyDictKeysObject *cached; assert(dictptr != NULL); if ((tp->tp_flags & Py_TPFLAGS_HEAPTYPE) && (cached = CACHED_KEYS(tp))) { assert(dictptr != NULL); dict = *dictptr; if (dict == NULL) { DK_INCREF(cached); dict = new_dict_with_shared_keys(cached); if (dict == NULL) return -1; *dictptr = dict; } if (value == NULL) { res = PyDict_DelItem(dict, key); // Since key sharing dict doesn't allow deletion, PyDict_DelItem() // always converts dict to combined form. if ((cached = CACHED_KEYS(tp)) != NULL) { CACHED_KEYS(tp) = NULL; DK_DECREF(cached); } } else { int was_shared = (cached == ((PyDictObject *)dict)->ma_keys); res = PyDict_SetItem(dict, key, value); if (was_shared && (cached = CACHED_KEYS(tp)) != NULL && cached != ((PyDictObject *)dict)->ma_keys) { /* PyDict_SetItem() may call dictresize and convert split table * into combined table. In such case, convert it to split * table again and update type's shared key only when this is * the only dict sharing key with the type. * * This is to allow using shared key in class like this: * * class C: * def __init__(self): * # one dict resize happens * self.a, self.b, self.c = 1, 2, 3 * self.d, self.e, self.f = 4, 5, 6 * a = C() */ if (cached->dk_refcnt == 1) { CACHED_KEYS(tp) = make_keys_shared(dict); } else { CACHED_KEYS(tp) = NULL; } DK_DECREF(cached); if (CACHED_KEYS(tp) == NULL && PyErr_Occurred()) return -1; } } } else { dict = *dictptr; if (dict == NULL) { dict = PyDict_New(); if (dict == NULL) return -1; *dictptr = dict; } if (value == NULL) { res = PyDict_DelItem(dict, key); } else { res = PyDict_SetItem(dict, key, value); } } return res; } void _PyDictKeys_DecRef(PyDictKeysObject *keys) { DK_DECREF(keys); } python-frozendict/src/frozendict/c_src/3_6/cpython_src/Objects/stringlib/000077500000000000000000000000001470250572200271075ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_6/cpython_src/Objects/stringlib/eq.h000066400000000000000000000015221470250572200276650ustar00rootroot00000000000000/* Fast unicode equal function optimized for dictobject.c and setobject.c */ /* Return 1 if two unicode objects are equal, 0 if not. * unicode_eq() is called when the hash of two unicode objects is equal. */ Py_LOCAL_INLINE(int) unicode_eq(PyObject *aa, PyObject *bb) { assert(PyUnicode_Check(aa)); assert(PyUnicode_Check(bb)); assert(! PyUnicode_READY(aa)); assert(! PyUnicode_READY(bb)); PyUnicodeObject *a = (PyUnicodeObject *)aa; PyUnicodeObject *b = (PyUnicodeObject *)bb; if (PyUnicode_GET_LENGTH(a) != PyUnicode_GET_LENGTH(b)) return 0; if (PyUnicode_GET_LENGTH(a) == 0) return 1; if (PyUnicode_KIND(a) != PyUnicode_KIND(b)) return 0; return memcmp(PyUnicode_1BYTE_DATA(a), PyUnicode_1BYTE_DATA(b), PyUnicode_GET_LENGTH(a) * PyUnicode_KIND(a)) == 0; } python-frozendict/src/frozendict/c_src/3_6/cpython_src/other.c000066400000000000000000000035011470250572200250050ustar00rootroot00000000000000static const unsigned int BitLengthTable[32] = { 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }; unsigned int _Py_bit_length(unsigned long d) { unsigned int d_bits = 0; while (d >= 32) { d_bits += 6; d >>= 6; } d_bits += BitLengthTable[d]; return d_bits; } #define Py_IS_TYPE(op, type) (Py_TYPE(op) == type) #define PySet_CheckExact(op) Py_IS_TYPE(op, &PySet_Type) #if defined(RANDALL_WAS_HERE) # define Py_UNREACHABLE() \ Py_FatalError( \ "If you're seeing this, the code is in what I thought was\n" \ "an unreachable state.\n\n" \ "I could give you advice for what to do, but honestly, why\n" \ "should you trust me? I clearly screwed this up. I'm writing\n" \ "a message that should never appear, yet I know it will\n" \ "probably appear someday.\n\n" \ "On a deep level, I know I'm not up to this task.\n" \ "I'm so sorry.\n" \ "https://xkcd.com/2200") #elif defined(Py_DEBUG) # define Py_UNREACHABLE() \ Py_FatalError( \ "We've reached an unreachable state. Anything is possible.\n" \ "The limits were in our heads all along. Follow your dreams.\n" \ "https://xkcd.com/2200") #elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) # define Py_UNREACHABLE() __builtin_unreachable() #elif defined(__clang__) || defined(__INTEL_COMPILER) # define Py_UNREACHABLE() __builtin_unreachable() #elif defined(_MSC_VER) # define Py_UNREACHABLE() __assume(0) #else # define Py_UNREACHABLE() \ Py_FatalError("Unreachable C code path reached") #endif #if defined(__GNUC__) \ && ((__GNUC__ >= 5) || (__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) #define _Py_HOT_FUNCTION __attribute__((hot)) #else #define _Py_HOT_FUNCTION #endif python-frozendict/src/frozendict/c_src/3_6/frozendictobject.c000066400000000000000000001614531470250572200247020ustar00rootroot00000000000000#include #include "frozendictobject.h" static PyObject* frozendict_iter(PyDictObject *dict); static int frozendict_equal(PyDictObject* a, PyDictObject* b); #include "other.c" #include "dictobject.c" static void frozendict_free_keys_object(PyDictKeysObject *keys, const int decref_items) { if (decref_items) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n; for (i = 0, n = keys->dk_nentries; i < n; i++) { Py_XDECREF(entries[i].me_key); Py_XDECREF(entries[i].me_value); } } #if PyDict_MAXFREELIST > 0 if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) { keys_free_list[numfreekeys++] = keys; return; } #endif PyObject_Free(keys); } static inline void frozendict_keys_decref(PyDictKeysObject *dk, const int decref_items) { assert(dk->dk_refcnt > 0); #ifdef Py_REF_DEBUG _Py_RefTotal--; #endif if (--dk->dk_refcnt == 0) { frozendict_free_keys_object(dk, decref_items); } } static int frozendict_resize(PyDictObject* mp, Py_ssize_t minsize) { const Py_ssize_t newsize = calculate_keysize(minsize); if (newsize <= 0) { PyErr_NoMemory(); return -1; } assert(IS_POWER_OF_2(newsize)); assert(newsize >= PyDict_MINSIZE); PyDictKeysObject* oldkeys = mp->ma_keys; /* Allocate a new table. */ PyDictKeysObject* new_keys = new_keys_object(newsize); if (new_keys == NULL) { return -1; } // New table must be large enough. assert(new_keys->dk_usable >= mp->ma_used); new_keys->dk_lookup = oldkeys->dk_lookup; const Py_ssize_t numentries = mp->ma_used; PyDictKeyEntry* newentries = DK_ENTRIES(new_keys); memcpy( newentries, DK_ENTRIES(oldkeys), numentries * sizeof(PyDictKeyEntry) ); build_indices(new_keys, newentries, numentries); new_keys->dk_usable -= numentries; new_keys->dk_nentries = numentries; // do not decref the keys inside! frozendict_keys_decref(oldkeys, 0); mp->ma_keys = new_keys; return 0; } static int frozendict_insert(PyDictObject *mp, PyObject *key, const Py_hash_t hash, PyObject *value, int empty) { PyObject **value_addr; Py_ssize_t ix; PyObject *old_value = NULL; PyDictKeysObject* keys = mp->ma_keys; Py_INCREF(key); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); if (! empty) { ix = keys->dk_lookup(mp, key, hash, &value_addr, NULL); if (value_addr != NULL) { old_value = *value_addr; } if (ix == DKIX_ERROR) { Py_DECREF(value); Py_DECREF(key); return -1; } empty = (ix == DKIX_EMPTY); } if (empty) { /* Insert into new slot. */ if (mp->ma_keys->dk_usable <= 0) { /* Need to resize. */ if (frozendict_resize(mp, GROWTH_RATE(mp))) { Py_DECREF(value); Py_DECREF(key); return -1; } // resize changes keys keys = mp->ma_keys; } const Py_ssize_t hashpos = find_empty_slot(keys, hash); const Py_ssize_t dk_nentries = keys->dk_nentries; PyDictKeyEntry* ep = &DK_ENTRIES(keys)[dk_nentries]; dictkeys_set_index(keys, hashpos, dk_nentries); ep->me_key = key; ep->me_hash = hash; ep->me_value = value; mp->ma_used++; keys->dk_usable--; keys->dk_nentries++; assert(keys->dk_usable >= 0); } else { DK_ENTRIES(mp->ma_keys)[ix].me_value = value; Py_DECREF(old_value); /* which **CAN** re-enter (see issue #22653) */ Py_DECREF(key); } ASSERT_CONSISTENT(mp); return 0; } static int frozendict_setitem(PyObject *op, PyObject *key, PyObject *value, int empty) { Py_hash_t hash; assert(key); assert(value); if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { return -1; } } return frozendict_insert((PyDictObject*) op, key, hash, value, empty); } // int _PyFrozendict_SetItem(PyObject *op, // PyObject *key, // PyObject *value, // int empty) { // if (! PyAnyFrozenDict_Check(op)) { // PyErr_BadInternalCall(); // return -1; // } // return frozendict_setitem(op, key, value, empty); // } static PyObject* _frozendict_new( PyTypeObject* type, PyObject* args, PyObject* kwds, const int use_empty_frozendict ); static PyObject * frozendict_fromkeys(PyObject *type, PyObject *args) { PyObject *iterable; PyObject *value = Py_None; if (!PyArg_UnpackTuple(args, "fromkeys", 1, 2, &iterable, &value)) return NULL; PyObject *it; /* iter(iterable) */ PyObject *key; PyObject *d; int status; d = _frozendict_new(&PyFrozenDict_Type, NULL, NULL, 0); if (d == NULL) return NULL; Py_ssize_t size; PyDictObject *mp = (PyDictObject *)d; mp->ma_keys = new_keys_object(PyDict_MINSIZE); if (PyAnyDict_CheckExact(iterable)) { PyObject *oldvalue; Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; size = ((PyDictObject *)iterable)->ma_used; if (mp->ma_keys->dk_usable < size) { if (frozendict_resize(mp, estimate_keysize(size))) { Py_DECREF(d); return NULL; } } while (_d_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) { if (frozendict_insert(mp, key, hash, value, 0)) { Py_DECREF(d); return NULL; } } return d; } else if (PyAnySet_CheckExact(iterable)) { Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; size = PySet_GET_SIZE(iterable); if (mp->ma_keys->dk_usable < size) { if (frozendict_resize(mp, estimate_keysize(size))) { Py_DECREF(d); return NULL; } } while (_PySet_NextEntry(iterable, &pos, &key, &hash)) { if (frozendict_insert(mp, key, hash, value, 0)) { Py_DECREF(d); return NULL; } } } else { it = PyObject_GetIter(iterable); if (it == NULL){ Py_DECREF(d); return NULL; } while ((key = PyIter_Next(it)) != NULL) { status = frozendict_setitem(d, key, value, 0); Py_DECREF(key); if (status < 0) { Py_DECREF(it); Py_DECREF(d); return NULL; } } Py_DECREF(it); if (PyErr_Occurred()) { Py_DECREF(d); return NULL; } } ASSERT_CONSISTENT(mp); if ((PyTypeObject*) type == &PyFrozenDict_Type) { return d; } PyObject* newargs = PyTuple_New(1); if (newargs == NULL) { Py_DECREF(d); return NULL; } PyTuple_SET_ITEM(newargs, 0, d); PyObject* res = PyObject_Call((PyObject*) type, newargs, NULL); Py_DECREF(newargs); return res; } /* Methods */ #define REPR_GENERIC_START "(" #define REPR_GENERIC_END ")" #define REPR_GENERIC_START_LEN 1 #define REPR_GENERIC_END_LEN 1 static PyObject* frozendict_repr(PyFrozenDictObject* mp) { PyObject* dict_repr_res = dict_repr((PyDictObject*) mp); if (dict_repr_res == NULL) { return NULL; } _PyUnicodeWriter writer; _PyUnicodeWriter_Init(&writer); int error = 0; PyObject* o = (PyObject*) mp; Py_ReprEnter(o); PyTypeObject* type = Py_TYPE(mp); size_t frozendict_name_len = strlen(type->tp_name); writer.min_length = ( frozendict_name_len + REPR_GENERIC_START_LEN + PyObject_Length(dict_repr_res) + REPR_GENERIC_END_LEN ); if (_PyUnicodeWriter_WriteASCIIString( &writer, type->tp_name, frozendict_name_len )) { error = 1; } else { if (_PyUnicodeWriter_WriteASCIIString( &writer, REPR_GENERIC_START, REPR_GENERIC_START_LEN )) { error = 1; } else { if (_PyUnicodeWriter_WriteStr(&writer, dict_repr_res)) { error = 1; } else { error = _PyUnicodeWriter_WriteASCIIString( &writer, REPR_GENERIC_END, REPR_GENERIC_END_LEN ); } } } Py_ReprLeave(o); Py_DECREF(dict_repr_res); if (error) { _PyUnicodeWriter_Dealloc(&writer); return NULL; } return _PyUnicodeWriter_Finish(&writer); } static PyMappingMethods frozendict_as_mapping = { (lenfunc)dict_length, /*mp_length*/ (binaryfunc)dict_subscript, /*mp_subscript*/ }; static int frozendict_merge(PyObject* a, PyObject* b, int empty) { /* We accept for the argument either a concrete dictionary object, * or an abstract "mapping" object. For the former, we can do * things quite efficiently. For the latter, we only require that * PyMapping_Keys() and PyObject_GetItem() be supported. */ assert(a != NULL); assert(PyAnyFrozenDict_Check(a)); assert(b != NULL); PyDictObject* mp = (PyDictObject*) a; if ( PyAnyDict_Check(b) && ( Py_TYPE(b)->tp_iter == PyDict_Type.tp_iter || Py_TYPE(b)->tp_iter == (getiterfunc)frozendict_iter ) ) { PyDictObject* other = (PyDictObject*)b; const Py_ssize_t numentries = other->ma_used; if (other == mp || numentries == 0) { /* a.update(a) or a.update({}); nothing to do */ return 0; } const int is_other_combined = other->ma_values == NULL; PyDictKeysObject* okeys = other->ma_keys; if ( empty && is_other_combined && numentries == okeys->dk_nentries ) { PyDictKeysObject *keys = clone_combined_dict_keys(other); if (keys == NULL) { return -1; } mp->ma_keys = keys; mp->ma_used = numentries; mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(mp); if (_PyObject_GC_IS_TRACKED(other) && !_PyObject_GC_IS_TRACKED(mp)) { PyObject_GC_Track(mp); } return 0; } PyDictKeyEntry* ep0 = DK_ENTRIES(okeys); PyDictKeyEntry* entry; PyObject* key; PyObject* value; Py_hash_t hash; int err; if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } /* Do one big resize at the start, rather than * incrementally resizing as we insert new items. Expect * that there will be no (or few) overlapping keys. */ if (mp->ma_keys->dk_usable < numentries) { if (frozendict_resize(mp, estimate_keysize(mp->ma_used + numentries))) { return -1; } } for (Py_ssize_t i = 0, n = okeys->dk_nentries; i < n; i++) { entry = &ep0[i]; key = entry->me_key; hash = entry->me_hash; if (is_other_combined) { value = entry->me_value; } else { value = other->ma_values[i]; } if (value != NULL) { Py_INCREF(key); Py_INCREF(value); err = frozendict_insert(mp, key, hash, value, empty); Py_DECREF(value); Py_DECREF(key); if (err != 0) { return -1; } if (n != other->ma_keys->dk_nentries) { PyErr_SetString(PyExc_RuntimeError, "dict mutated during update"); return -1; } } } } else { /* Do it the generic, slower way */ PyObject *keys = PyMapping_Keys(b); PyObject *iter; PyObject *key, *value; int status; if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } if (keys == NULL) /* Docstring says this is equivalent to E.keys() so * if E doesn't have a .keys() method we want * AttributeError to percolate up. Might as well * do the same for any other error. */ return -1; iter = PyObject_GetIter(keys); Py_DECREF(keys); if (iter == NULL) return -1; for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) { value = PyObject_GetItem(b, key); if (value == NULL) { Py_DECREF(iter); Py_DECREF(key); return -1; } status = frozendict_setitem(a, key, value, 0); Py_DECREF(key); Py_DECREF(value); if (status < 0) { Py_DECREF(iter); return -1; } } Py_DECREF(iter); if (PyErr_Occurred()) /* Iterator completed, via error */ return -1; } ASSERT_CONSISTENT(a); return 0; } static int frozendict_merge_from_seq2(PyObject* d, PyObject* seq2) { assert(d != NULL); assert(PyAnyFrozenDict_Check(d)); assert(seq2 != NULL); PyObject* it = PyObject_GetIter(seq2); if (it == NULL) { return -1; } PyObject* fast; /* item as a 2-tuple or 2-list */ PyObject* key = NULL; PyObject* value = NULL; Py_ssize_t n; PyObject* item; int res = 0; PyDictObject* mp = (PyDictObject*) d; if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } for (Py_ssize_t i = 0; ; ++i) { fast = NULL; item = PyIter_Next(it); if (item == NULL) { if (PyErr_Occurred()) { res = -1; } break; } /* Convert item to sequence, and verify length 2. */ fast = PySequence_Fast(item, ""); if (fast == NULL) { if (PyErr_ExceptionMatches(PyExc_TypeError)) { PyErr_Format(PyExc_TypeError, "cannot convert dictionary update " "sequence element #%zd to a sequence", i); } Py_DECREF(item); res = -1; break; } n = PySequence_Fast_GET_SIZE(fast); if (n != 2) { PyErr_Format(PyExc_ValueError, "dictionary update sequence element #%zd " "has length %zd; 2 is required", i, n); Py_DECREF(fast); Py_DECREF(item); res = -1; break; } /* Update/merge with this (key, value) pair. */ key = PySequence_Fast_GET_ITEM(fast, 0); Py_INCREF(key); value = PySequence_Fast_GET_ITEM(fast, 1); Py_INCREF(value); if (frozendict_setitem(d, key, value, 0) < 0) { Py_DECREF(key); Py_DECREF(value); Py_DECREF(fast); Py_DECREF(item); res = -1; break; } Py_DECREF(key); Py_DECREF(value); Py_DECREF(fast); Py_DECREF(item); } Py_DECREF(it); ASSERT_CONSISTENT(d); return res; } static int frozendict_update_arg(PyObject *self, PyObject *arg, const int empty) { if (PyAnyDict_CheckExact(arg)) { return frozendict_merge(self, arg, empty); } _Py_IDENTIFIER(keys); if (_PyObject_HasAttrId(arg, &PyId_keys)) { return frozendict_merge(self, arg, empty); } return frozendict_merge_from_seq2(self, arg); } static int frozendict_update_common(PyObject* self, PyObject* arg, PyObject* kwds) { int result = 0; const int no_arg = (arg == NULL); if (! no_arg) { result = frozendict_update_arg(self, arg, 1); } if (result == 0 && kwds != NULL) { if (PyArg_ValidateKeywordArguments(kwds)) { result = frozendict_merge(self, kwds, no_arg); } else { result = -1; } } return result; } /* Forward */ static PyObject *frozendictkeys_new(PyObject *, PyObject *); static PyObject *frozendictitems_new(PyObject *, PyObject *); static PyObject *frozendictvalues_new(PyObject *, PyObject *); #define MINUSONE_HASH ((Py_hash_t) -1) static Py_hash_t frozendict_hash(PyObject* self) { PyFrozenDictObject* frozen_self = (PyFrozenDictObject*) self; Py_hash_t hash; if (frozen_self->ma_hash != MINUSONE_HASH) { hash = frozen_self->ma_hash; } else { PyObject* frozen_items_tmp = frozendictitems_new(self, NULL); int save_hash = 1; if (frozen_items_tmp == NULL) { hash = MINUSONE_HASH; save_hash = 0; } else { PyObject* frozen_items = PyFrozenSet_New(frozen_items_tmp); Py_DECREF(frozen_items_tmp); if (frozen_items == NULL) { hash = MINUSONE_HASH; save_hash = 0; } else { hash = PyFrozenSet_Type.tp_hash(frozen_items); Py_DECREF(frozen_items); } } if (save_hash) { frozen_self->ma_hash = hash; } } return hash; } static PyObject* frozendict_copy(PyObject* o, PyObject* Py_UNUSED(ignored)) { if (PyAnyFrozenDict_CheckExact(o)) { Py_INCREF(o); return o; } PyObject* args = PyTuple_New(1); if (args == NULL) { return NULL; } Py_INCREF(o); PyTuple_SET_ITEM(args, 0, o); PyTypeObject* type = Py_TYPE(o); PyObject* res = PyObject_Call((PyObject *) type, args, NULL); Py_DECREF(args); return res; } PyObject* frozendict_deepcopy(PyObject* self, PyObject* memo) { if (PyAnyFrozenDict_CheckExact(self)) { frozendict_hash(self); if (PyErr_Occurred()) { PyErr_Clear(); } else { Py_INCREF(self); return self; } } if (! PyAnyFrozenDict_Check(self)) { Py_RETURN_NOTIMPLEMENTED; } PyObject* d = PyDict_New(); if (d == NULL) { return NULL; } PyObject* copy_module_name = NULL; PyObject* copy_module = NULL; PyObject* deepcopy_fun = NULL; PyObject* deep_args = NULL; PyObject* deep_d = NULL; PyObject* args = NULL; PyObject* res = NULL; int decref_d = 1; int decref_deep_d = 1; if (PyDict_Merge(d, self, 1)) { goto end; } copy_module_name = PyUnicode_FromString("copy"); if (copy_module_name == NULL) { goto end; } copy_module = PyImport_Import(copy_module_name); if (copy_module == NULL) { goto end; } deepcopy_fun = PyObject_GetAttrString(copy_module, "deepcopy"); if (deepcopy_fun == NULL) { goto end; } deep_args = PyTuple_New(2); if (deep_args == NULL) { goto end; } PyTuple_SET_ITEM(deep_args, 0, d); decref_d = 0; Py_INCREF(memo); PyTuple_SET_ITEM(deep_args, 1, memo); deep_d = PyObject_CallObject(deepcopy_fun, deep_args); if (deep_d == NULL) { goto end; } args = PyTuple_New(1); if (args == NULL) { goto end; } PyTuple_SET_ITEM(args, 0, deep_d); decref_deep_d = 0; PyTypeObject* type = Py_TYPE(self); res = PyObject_Call((PyObject *) type, args, NULL); end: Py_XDECREF(args); Py_XDECREF(deep_args); Py_XDECREF(deepcopy_fun); Py_XDECREF(copy_module); Py_XDECREF(copy_module_name); if (decref_d) { Py_DECREF(d); } if (decref_deep_d) { Py_DECREF(deep_d); } return res; } static int frozendict_equal(PyDictObject* a, PyDictObject* b) { if (a == b) { return 1; } if (a->ma_used != b->ma_used) { /* can't be equal if # of entries differ */ return 0; } PyDictKeysObject* keys = a->ma_keys; PyDictKeyEntry* ep; PyObject* aval; int cmp = 1; PyObject** bval; PyObject* key; /* Same # of entries -- check all of 'em. Exit early on any diff. */ for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) { ep = &DK_ENTRIES(keys)[i]; aval = ep->me_value; Py_INCREF(aval); key = ep->me_key; Py_INCREF(key); /* reuse the known hash value */ b->ma_keys->dk_lookup(b, key, ep->me_hash, &bval, NULL); if (bval == NULL) { if (PyErr_Occurred()) { cmp = -1; } else { cmp = 0; } } else { Py_INCREF(*bval); cmp = PyObject_RichCompareBool(aval, *bval, Py_EQ); Py_DECREF(*bval); } Py_DECREF(key); Py_DECREF(aval); if (cmp <= 0) { /* error or not equal */ break; } } return cmp; } static Py_ssize_t dict_get_index(PyDictObject *self, PyObject *key) { Py_hash_t hash; PyObject** val; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return DKIX_ERROR; } return (self->ma_keys->dk_lookup) (self, key, hash, &val, NULL); } static PyObject * frozendict_reduce(PyFrozenDictObject* mp, PyObject *Py_UNUSED(ignored)) { PyObject *d = PyDict_New(); if (d == NULL) { return NULL; } if (PyDict_Merge(d, (PyObject *)mp, 1)) { Py_DECREF(d); return NULL; } return Py_BuildValue("O(N)", Py_TYPE(mp), d); } static PyObject* frozendict_clone(PyObject* self) { PyTypeObject* type = Py_TYPE(self); PyObject* new_op = type->tp_alloc(type, 0); if (new_op == NULL){ return NULL; } if (type == &PyFrozenDict_Type) { PyObject_GC_UnTrack(new_op); } PyDictObject* mp = (PyDictObject*) self; PyDictKeysObject *keys = clone_combined_dict_keys(mp); if (keys == NULL) { return NULL; } PyFrozenDictObject* new_mp = (PyFrozenDictObject*) new_op; new_mp->ma_keys = keys; if (_PyObject_GC_IS_TRACKED(mp) && !_PyObject_GC_IS_TRACKED(new_mp)) { PyObject_GC_Track(new_mp); } new_mp->ma_used = mp->ma_used; new_mp->ma_hash = MINUSONE_HASH; new_mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(new_mp); return new_op; } static PyObject* frozendict_set( PyObject* self, PyObject* args ) { PyObject* set_key; PyObject* set_val; if (! PyArg_UnpackTuple(args, "set", 2, 2, &set_key, &set_val)) { return NULL; } PyObject* new_op = frozendict_clone(self); if (new_op == NULL) { return NULL; } if (frozendict_setitem(new_op, set_key, set_val, 0)) { Py_DECREF(new_op); return NULL; } if ( ((PyDictObject*) self)->ma_keys->dk_lookup == lookdict_unicode_nodummy && ! PyUnicode_CheckExact(set_key) ) { ((PyFrozenDictObject*) new_op)->ma_keys->dk_lookup = lookdict; } return new_op; } static PyObject* frozendict_setdefault( PyObject* self, PyObject* args ) { PyObject* set_key; PyObject* val = Py_None; if (! PyArg_UnpackTuple(args, "setdefault", 1, 2, &set_key, &val)) { return NULL; } if (PyDict_Contains(self, set_key)) { Py_INCREF(self); return self; } PyObject* new_op = frozendict_clone(self); if (new_op == NULL) { return NULL; } if (frozendict_setitem(new_op, set_key, val, 0)) { Py_DECREF(new_op); return NULL; } if ( ((PyDictObject*) self)->ma_keys->dk_lookup == lookdict_unicode_nodummy && ! PyUnicode_CheckExact(set_key) ) { ((PyFrozenDictObject*) new_op)->ma_keys->dk_lookup = lookdict; } return new_op; } static PyObject* frozendict_delete( PyObject* self, PyObject* del_key ) { PyDictObject* mp = (PyDictObject*) self; const Py_ssize_t ix = dict_get_index(mp, del_key); if (ix == DKIX_ERROR) { return NULL; } if (ix == DKIX_EMPTY) { _PyErr_SetKeyError(del_key); return NULL; } const Py_ssize_t size = mp->ma_used; const Py_ssize_t sizemm = size - 1; if (sizemm == 0) { PyObject* args = PyTuple_New(0); if (args == NULL) { return NULL; } return PyObject_Call((PyObject*) Py_TYPE(self), args, NULL); } PyTypeObject* type = Py_TYPE(self); PyObject* new_op = type->tp_alloc(type, 0); if (new_op == NULL) { return NULL; } if (type == &PyFrozenDict_Type) { PyObject_GC_UnTrack(new_op); } const Py_ssize_t newsize = estimate_keysize(sizemm); if (newsize <= 0) { Py_DECREF(new_op); PyErr_NoMemory(); return NULL; } assert(IS_POWER_OF_2(newsize)); assert(newsize >= PyDict_MINSIZE); /* Allocate a new table. */ PyDictKeysObject* new_keys = new_keys_object(newsize); if (new_keys == NULL) { Py_DECREF(new_op); return NULL; } const PyDictKeysObject* old_keys = mp->ma_keys; new_keys->dk_lookup = old_keys->dk_lookup; PyFrozenDictObject* new_mp = (PyFrozenDictObject*) new_op; // New table must be large enough. assert(new_keys->dk_usable >= new_mp->ma_used); new_mp->ma_keys = new_keys; new_mp->ma_hash = MINUSONE_HASH; new_mp->ma_version_tag = DICT_NEXT_VERSION(); PyObject* key; PyObject* value; Py_hash_t hash; Py_ssize_t hashpos; PyDictKeyEntry* old_entries = DK_ENTRIES(old_keys); PyDictKeyEntry* new_entries = DK_ENTRIES(new_keys); PyDictKeyEntry* old_entry; PyDictKeyEntry* new_entry; Py_ssize_t new_i; int deleted = 0; for (Py_ssize_t i = 0; i < size; i++) { if (i == ix) { deleted = 1; continue; } new_i = i - deleted; old_entry = &old_entries[i]; hash = old_entry->me_hash; key = old_entry->me_key; value = old_entry->me_value; Py_INCREF(key); Py_INCREF(value); hashpos = find_empty_slot(new_keys, hash); dictkeys_set_index(new_keys, hashpos, new_i); new_entry = &new_entries[new_i]; new_entry->me_key = key; new_entry->me_hash = hash; new_entry->me_value = value; } new_mp->ma_used = sizemm; new_keys->dk_usable -= sizemm; new_keys->dk_nentries = sizemm; ASSERT_CONSISTENT(new_mp); return new_op; } static const PyObject* frozendict_key( PyObject* self, PyObject* args ) { PyObject* i = NULL; if (! PyArg_UnpackTuple(args, "key", 0, 1, &i)) { return NULL; } Py_ssize_t index; Py_ssize_t passed_index; PyDictObject* d = (PyDictObject*) self; const Py_ssize_t size = d->ma_used; if (i != NULL) { index = PyLong_AsSsize_t(i); passed_index = index; if (index < 0) { if (PyErr_Occurred()) { return NULL; } index += size; } } else { index = 0; passed_index = index; } const Py_ssize_t maxindex = size - 1; if (index > maxindex || index < 0) { PyErr_Format( PyExc_IndexError, "%s index %zd out of range %zd", Py_TYPE(self)->tp_name, passed_index, maxindex ); return NULL; } const PyObject* res = DK_ENTRIES(d->ma_keys)[index].me_key; Py_INCREF(res); return res; } static const PyObject* frozendict_value( PyObject* self, PyObject* args ) { PyObject* i = NULL; if (! PyArg_UnpackTuple(args, "value", 0, 1, &i)) { return NULL; } Py_ssize_t index; Py_ssize_t passed_index; PyDictObject* d = (PyDictObject*) self; const Py_ssize_t size = d->ma_used; if (i != NULL) { index = PyLong_AsSsize_t(i); passed_index = index; if (index < 0) { if (PyErr_Occurred()) { return NULL; } index += size; } } else { index = 0; passed_index = index; } const Py_ssize_t maxindex = size - 1; if (index > maxindex || index < 0) { PyErr_Format( PyExc_IndexError, "%s index %zd out of range %zd", Py_TYPE(self)->tp_name, passed_index, maxindex ); return NULL; } const PyObject* res = DK_ENTRIES(d->ma_keys)[index].me_value; Py_INCREF(res); return res; } static const PyObject* frozendict_item( PyObject* self, PyObject* args ) { PyObject* i = NULL; if (! PyArg_UnpackTuple(args, "item", 0, 1, &i)) { return NULL; } Py_ssize_t index; Py_ssize_t passed_index; PyDictObject* d = (PyDictObject*) self; const Py_ssize_t size = d->ma_used; if (i != NULL) { index = PyLong_AsSsize_t(i); passed_index = index; if (index < 0) { if (PyErr_Occurred()) { return NULL; } index += size; } } else { index = 0; passed_index = index; } const Py_ssize_t maxindex = size - 1; if (index > maxindex || index < 0) { PyErr_Format( PyExc_IndexError, "%s index %zd out of range %zd", Py_TYPE(self)->tp_name, passed_index, maxindex ); return NULL; } PyObject* key = DK_ENTRIES(d->ma_keys)[index].me_key; Py_INCREF(key); PyObject* val = DK_ENTRIES(d->ma_keys)[index].me_value; Py_INCREF(val); const PyObject* res = PyTuple_New(2); PyTuple_SET_ITEM(res, 0, key); PyTuple_SET_ITEM(res, 1, val); return res; } PyDoc_STRVAR(frozendict_set_doc, "set($self, key, value, /)\n" "--\n" "\n" "Returns a copy of the dictionary with the new (key, value) item. "); PyDoc_STRVAR(frozendict_setdefault_doc, "set($self, key[, default], /)\n" "--\n" "\n" "If key is in the dictionary, it returns the dictionary unchanged. \n" "Otherwise, it returns a copy of the dictionary with the new (key, default) item; \n" "default argument is optional and is None by default. "); PyDoc_STRVAR(frozendict_delete_doc, "delete($self, key, /)\n" "--\n" "\n" "Returns a copy of the dictionary without the item of the corresponding key. "); PyDoc_STRVAR(frozendict_key_doc, "key($self[, index], /)\n" "--\n" "\n" "Get the key at the specified index (insertion order). If index is not \n" "passed, it defaults to 0. If index is negative, returns the key at \n" "position size + index. "); PyDoc_STRVAR(frozendict_value_doc, "value($self[, index], /)\n" "--\n" "\n" "Get the value at the specified index (insertion order). If index is not \n" "passed, it defaults to 0. If index is negative, returns the value at \n" "position size + index. "); PyDoc_STRVAR(frozendict_item_doc, "item($self[, index], /)\n" "--\n" "\n" "Get the (key, value) item at the specified index (insertion order). If \n" "index is not passed, it defaults to 0. If index is negative, returns the \n" "item at position size + index. "); static PyMethodDef frozendict_mapp_methods[] = { DICT___CONTAINS___METHODDEF {"__getitem__", (PyCFunction)(void(*)(void))dict_subscript, METH_O | METH_COEXIST, getitem__doc__}, {"__sizeof__", (PyCFunction)(void(*)(void))dict_sizeof, METH_NOARGS, sizeof__doc__}, DICT_GET_METHODDEF {"keys", frozendictkeys_new, METH_NOARGS, keys__doc__}, {"items", frozendictitems_new, METH_NOARGS, items__doc__}, {"values", frozendictvalues_new, METH_NOARGS, values__doc__}, {"fromkeys", (PyCFunction)frozendict_fromkeys, METH_VARARGS|METH_CLASS, dict_fromkeys__doc__}, {"copy", (PyCFunction)frozendict_copy, METH_NOARGS, copy__doc__}, {"__copy__", (PyCFunction)frozendict_copy, METH_NOARGS, "Returns a copy of the object."}, {"__deepcopy__", (PyCFunction)frozendict_deepcopy, METH_O, "Returns a deepcopy of the object."}, DICT___REVERSED___METHODDEF {"__reduce__", (PyCFunction)(void(*)(void))frozendict_reduce, METH_NOARGS, ""}, {"set", (PyCFunction) frozendict_set, METH_VARARGS, frozendict_set_doc}, {"setdefault", (PyCFunction) frozendict_setdefault, METH_VARARGS, frozendict_setdefault_doc}, {"delete", (PyCFunction) frozendict_delete, METH_O, frozendict_delete_doc}, {"key", (PyCFunction) frozendict_key, METH_VARARGS, frozendict_key_doc}, {"value", (PyCFunction) frozendict_value, METH_VARARGS, frozendict_value_doc}, {"item", (PyCFunction) frozendict_item, METH_VARARGS, frozendict_item_doc}, {NULL, NULL} /* sentinel */ }; static PyObject* frozendict_new_barebone(PyTypeObject* type) { PyObject* self = type->tp_alloc(type, 0); if (self == NULL) { return NULL; } /* The object has been implicitly tracked by tp_alloc */ if (type == &PyFrozenDict_Type) { PyObject_GC_UnTrack(self); } PyFrozenDictObject* mp = (PyFrozenDictObject*) self; mp->ma_keys = NULL; mp->ma_values = NULL; mp->ma_used = 0; mp->ma_hash = MINUSONE_HASH; return self; } // empty frozendict singleton static PyObject* empty_frozendict = NULL; // if frozendict is empty, return the empty singleton static PyObject* frozendict_create_empty( PyFrozenDictObject* mp, const PyTypeObject* type, const int use_empty_frozendict ) { if (mp->ma_used == 0) { if (use_empty_frozendict && type == &PyFrozenDict_Type) { if (empty_frozendict == NULL) { empty_frozendict = (PyObject*) mp; Py_INCREF(Py_EMPTY_KEYS); ((PyDictObject*) empty_frozendict)->ma_keys = Py_EMPTY_KEYS; mp->ma_version_tag = DICT_NEXT_VERSION(); } else { Py_DECREF(mp); } Py_INCREF(empty_frozendict); return empty_frozendict; } else { if (mp->ma_keys != NULL) { frozendict_keys_decref(mp->ma_keys, 0); } Py_INCREF(Py_EMPTY_KEYS); mp->ma_keys = Py_EMPTY_KEYS; return NULL; } } return NULL; } static PyObject* _frozendict_new( PyTypeObject* type, PyObject* args, PyObject* kwds, const int use_empty_frozendict ) { assert(type != NULL && type->tp_alloc != NULL); PyObject* arg = NULL; if (args != NULL && ! PyArg_UnpackTuple(args, "dict", 0, 1, &arg)) { return NULL; } const int arg_is_frozendict = (arg != NULL && PyAnyFrozenDict_CheckExact(arg)); const int kwds_size = ((kwds != NULL) ? ((PyDictObject*) kwds)->ma_used : 0 ); // only argument is a frozendict if (arg_is_frozendict && kwds_size == 0 && type == &PyFrozenDict_Type) { Py_INCREF(arg); return arg; } PyObject* self = frozendict_new_barebone(type); PyFrozenDictObject* mp = (PyFrozenDictObject*) self; if (frozendict_update_common(self, arg, kwds)) { Py_DECREF(self); return NULL; } PyObject* empty = frozendict_create_empty(mp, type, use_empty_frozendict); if (empty != NULL) { return empty; } mp->ma_version_tag = DICT_NEXT_VERSION(); return self; } static PyObject* frozendict_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { return _frozendict_new(type, args, kwds, 1); } static PyObject* frozendict_or(PyObject *self, PyObject *other) { if (! PyAnyFrozenDict_Check(self) || ! PyAnyDict_Check(other)) { Py_RETURN_NOTIMPLEMENTED; } PyObject* new = frozendict_clone(self); if (new == NULL) { return NULL; } if (frozendict_update_arg(new, other, 0)) { Py_DECREF(new); return NULL; } return new; } static PyNumberMethods frozendict_as_number = { .nb_or = frozendict_or, }; #define FROZENDICT_CLASS_NAME "frozendict" #define FROZENDICT_MODULE_NAME "frozendict" #define FROZENDICT_FULL_NAME FROZENDICT_MODULE_NAME "." FROZENDICT_CLASS_NAME PyDoc_STRVAR(frozendict_doc, "An immutable version of dict.\n" "\n" FROZENDICT_FULL_NAME "() -> returns an empty immutable dictionary\n" FROZENDICT_FULL_NAME "(mapping) -> returns an immutable dictionary initialized from a mapping object's\n" " (key, value) pairs\n" FROZENDICT_FULL_NAME "(iterable) -> returns an immutable dictionary, equivalent to:\n" " d = {}\n" " " " for k, v in iterable:\n" " d[k] = v\n" " " " " FROZENDICT_FULL_NAME "(d)\n" FROZENDICT_FULL_NAME "(**kwargs) -> returns an immutable dictionary initialized with the name=value pairs\n" " in the keyword argument list. For example: " FROZENDICT_FULL_NAME "(one=1, two=2)"); static PyTypeObject PyFrozenDict_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_FULL_NAME, /* tp_name */ sizeof(PyFrozenDictObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)dict_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)frozendict_repr, /* tp_repr */ &frozendict_as_number, /* tp_as_number */ &dict_as_sequence, /* tp_as_sequence */ &frozendict_as_mapping, /* tp_as_mapping */ (hashfunc)frozendict_hash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ frozendict_doc, /* tp_doc */ dict_traverse, /* tp_traverse */ 0, /* tp_clear */ dict_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendict_iter, /* tp_iter */ 0, /* tp_iternext */ frozendict_mapp_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ frozendict_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ }; /* Dictionary iterator types */ static PyObject* frozendict_iter(PyDictObject *dict) { return dictiter_new(dict, &PyFrozenDictIterKey_Type); } static PyObject* frozendictiter_iternextkey(dictiterobject* di) { Py_ssize_t pos = di->di_pos; assert(pos >= 0); PyDictObject* d = di->di_dict; if (d == NULL) { return NULL; } assert(PyAnyFrozenDict_Check(d)); if (pos >= d->ma_used) { di->di_dict = NULL; Py_DECREF(d); return NULL; } PyObject* key = DK_ENTRIES(d->ma_keys)[pos].me_key; assert(key != NULL); di->di_pos++; di->len--; Py_INCREF(key); return key; } static PyTypeObject PyFrozenDictIterKey_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".keyiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)frozendictiter_iternextkey, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject* frozendictiter_iternextvalue(dictiterobject* di) { Py_ssize_t pos = di->di_pos; assert(pos >= 0); PyDictObject* d = di->di_dict; if (d == NULL) { return NULL; } assert(PyAnyFrozenDict_Check(d)); if (pos >= d->ma_used) { di->di_dict = NULL; Py_DECREF(d); return NULL; } PyObject* val = DK_ENTRIES(d->ma_keys)[pos].me_value; assert(val != NULL); di->di_pos++; di->len--; Py_INCREF(val); return val; } static PyTypeObject PyFrozenDictIterValue_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".valueiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)frozendictiter_iternextvalue, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject* frozendictiter_iternextitem(dictiterobject* di) { Py_ssize_t pos = di->di_pos; assert(pos >= 0); PyDictObject* d = di->di_dict; if (d == NULL) { return NULL; } assert(PyAnyFrozenDict_Check(d)); if (pos >= d->ma_used) { di->di_dict = NULL; Py_DECREF(d); return NULL; } PyDictKeyEntry* entry_ptr = &DK_ENTRIES(d->ma_keys)[pos]; PyObject* key = entry_ptr->me_key; PyObject* val = entry_ptr->me_value; assert(key != NULL); assert(val != NULL); di->di_pos++; di->len--; Py_INCREF(key); Py_INCREF(val); PyObject* result; if (Py_REFCNT(di->di_result) == 1) { result = di->di_result; PyObject *oldkey = PyTuple_GET_ITEM(result, 0); PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); if (!_PyObject_GC_IS_TRACKED(result)) { PyObject_GC_Track(result); } } else { result = PyTuple_New(2); if (result == NULL) return NULL; } PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, val); /* steals reference */ return result; } static PyTypeObject PyFrozenDictIterItem_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".itemiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)frozendictiter_iternextitem, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; /*** dict_keys ***/ static PyObject * frozendictkeys_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyFrozenDictIterKey_Type); } static PyTypeObject PyFrozenDictKeys_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".keys", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictkeys_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendictkeys_iter, /* tp_iter */ 0, /* tp_iternext */ dictkeys_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * frozendictkeys_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _d_PyDictView_New(dict, &PyFrozenDictKeys_Type); } /*** dict_items ***/ static PyObject * frozendictitems_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyFrozenDictIterItem_Type); } static PyTypeObject PyFrozenDictItems_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".items", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictitems_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendictitems_iter, /* tp_iter */ 0, /* tp_iternext */ dictitems_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * frozendictitems_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _d_PyDictView_New(dict, &PyFrozenDictItems_Type); } /*** dict_values ***/ static PyObject * frozendictvalues_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyFrozenDictIterValue_Type); } static PyTypeObject PyFrozenDictValues_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".values", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ 0, /* tp_as_number */ &dictvalues_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendictvalues_iter, /* tp_iter */ 0, /* tp_iternext */ dictvalues_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * frozendictvalues_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _d_PyDictView_New(dict, &PyFrozenDictValues_Type); } static int frozendict_exec(PyObject *m) { /* Finalize the type object including setting type of the new type * object; doing it here is required for portability, too. */ if (PyType_Ready(&PyFrozenDict_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictIterKey_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictIterValue_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictIterItem_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictKeys_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictItems_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictValues_Type) < 0) { goto fail; } if (PyType_Ready(&PyDictRevIterKey_Type) < 0) { goto fail; } if (PyType_Ready(&PyDictRevIterItem_Type) < 0) { goto fail; } if (PyType_Ready(&PyDictRevIterValue_Type) < 0) { goto fail; } PyModule_AddObject(m, FROZENDICT_CLASS_NAME, (PyObject *)&PyFrozenDict_Type); return 0; fail: Py_XDECREF(m); return -1; } static struct PyModuleDef_Slot frozendict_slots[] = { {Py_mod_exec, frozendict_exec}, {0, NULL}, }; static struct PyModuleDef frozendictmodule = { PyModuleDef_HEAD_INIT, FROZENDICT_MODULE_NAME, /* name of module */ NULL, /* module documentation, may be NULL */ 0, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ NULL, frozendict_slots, NULL, NULL, NULL }; PyMODINIT_FUNC PyInit__frozendict(void) { return PyModuleDef_Init(&frozendictmodule); } python-frozendict/src/frozendict/c_src/3_7/000077500000000000000000000000001470250572200211675ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_7/Include/000077500000000000000000000000001470250572200225525ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_7/Include/cpython/000077500000000000000000000000001470250572200242365ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_7/Include/cpython/frozendictobject.h000066400000000000000000000004601470250572200277450ustar00rootroot00000000000000#ifndef Py_CPYTHON_FROZENDICTOBJECT_H # error "this header file must not be included directly" #endif typedef struct { PyObject_HEAD Py_ssize_t ma_used; uint64_t ma_version_tag; PyDictKeysObject* ma_keys; PyObject** ma_values; Py_hash_t ma_hash; } PyFrozenDictObject; python-frozendict/src/frozendict/c_src/3_7/Include/frozendictobject.h000066400000000000000000000050501470250572200262610ustar00rootroot00000000000000#ifndef Py_FROZENDICTOBJECT_H #define Py_FROZENDICTOBJECT_H #ifdef __cplusplus extern "C" { #endif // PyAPI_DATA(PyTypeObject) PyFrozenDict_Type; static PyTypeObject PyFrozenDict_Type; // PyAPI_DATA(PyTypeObject) PyCoold_Type; static PyTypeObject PyCoold_Type; #define PyFrozenDict_Check(op) \ ( \ Py_IS_TYPE(op, &PyFrozenDict_Type) \ || PyType_IsSubtype(Py_TYPE(op), &PyFrozenDict_Type) \ ) #define PyFrozenDict_CheckExact(op) Py_IS_TYPE(op, &PyFrozenDict_Type) #define PyAnyFrozenDict_Check(op) \ ( \ Py_IS_TYPE(op, &PyFrozenDict_Type) \ || Py_IS_TYPE(op, &PyCoold_Type) \ || PyType_IsSubtype(Py_TYPE(op), &PyFrozenDict_Type) \ || PyType_IsSubtype(Py_TYPE(op), &PyCoold_Type) \ ) #define PyAnyFrozenDict_CheckExact(op) \ ( \ Py_IS_TYPE(op, &PyFrozenDict_Type) \ || Py_IS_TYPE(op, &PyCoold_Type) \ ) #define PyAnyDict_Check(ob) \ ( \ PyDict_Check(ob) \ || Py_IS_TYPE(ob, &PyFrozenDict_Type) \ || Py_IS_TYPE(ob, &PyCoold_Type) \ || PyType_IsSubtype(Py_TYPE(ob), &PyFrozenDict_Type) \ || PyType_IsSubtype(Py_TYPE(ob), &PyCoold_Type) \ ) #define PyAnyDict_CheckExact(op) ( \ (Py_IS_TYPE(op, &PyDict_Type)) \ || (Py_IS_TYPE(op, &PyFrozenDict_Type)) \ || (Py_IS_TYPE(op, &PyCoold_Type)) \ ) // PyAPI_DATA(PyTypeObject) PyFrozenDictIterKey_Type; static PyTypeObject PyFrozenDictIterKey_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictIterValues_Type; static PyTypeObject PyFrozenDictIterValue_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictIterItem_Type; static PyTypeObject PyFrozenDictIterItem_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictKeys_Type; static PyTypeObject PyFrozenDictKeys_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictValues_Type; static PyTypeObject PyFrozenDictValues_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictItems_Type; static PyTypeObject PyFrozenDictItems_Type; #define PyAnyDictKeys_Check(op) (PyDictKeys_Check(op) || PyObject_TypeCheck(op, &PyFrozenDictKeys_Type)) #define PyAnyDictValues_Check(op) (PyDictValues_Check(op) || PyObject_TypeCheck(op, &PyFrozenDictValues_Type)) #define PyAnyDictItems_Check(op) (PyDictItems_Check(op) || PyObject_TypeCheck(op, &PyFrozenDictItems_Type)) /* This excludes Values, since they are not sets. */ # define PyAnyDictViewSet_Check(op) \ (PyAnyDictKeys_Check(op) || PyAnyDictItems_Check(op)) #ifndef Py_LIMITED_API # define Py_CPYTHON_FROZENDICTOBJECT_H # include "cpython/frozendictobject.h" # undef Py_CPYTHON_FROZENDICTOBJECT_H #endif #ifdef __cplusplus } #endif #endif python-frozendict/src/frozendict/c_src/3_7/cpython_src/000077500000000000000000000000001470250572200235225ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_7/cpython_src/Objects/000077500000000000000000000000001470250572200251135ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_7/cpython_src/Objects/clinic/000077500000000000000000000000001470250572200263545ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_7/cpython_src/Objects/clinic/dictobject.c.h000066400000000000000000000046551470250572200310720ustar00rootroot00000000000000/*[clinic input] preserve [clinic start generated code]*/ PyDoc_STRVAR(dict_fromkeys__doc__, "fromkeys($type, iterable, value=None, /)\n" "--\n" "\n" "Create a new dictionary with keys from iterable and values set to value."); #define DICT_FROMKEYS_METHODDEF \ {"fromkeys", (PyCFunction)(void(*)(void))dict_fromkeys, METH_FASTCALL|METH_CLASS, dict_fromkeys__doc__}, static PyObject * frozendict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value); static PyObject * dict_fromkeys(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; PyObject *iterable; PyObject *value = Py_None; if (!_PyArg_UnpackStack(args, nargs, "fromkeys", 1, 2, &iterable, &value)) { goto exit; } return_value = frozendict_fromkeys_impl(type, iterable, value); exit: return return_value; } PyDoc_STRVAR(dict___contains____doc__, "__contains__($self, key, /)\n" "--\n" "\n" "True if the dictionary has the specified key, else False."); #define DICT___CONTAINS___METHODDEF \ {"__contains__", (PyCFunction)dict___contains__, METH_O|METH_COEXIST, dict___contains____doc__}, PyDoc_STRVAR(dict_get__doc__, "get($self, key, default=None, /)\n" "--\n" "\n" "Return the value for key if key is in the dictionary, else default."); #define DICT_GET_METHODDEF \ {"get", (PyCFunction)(void(*)(void))dict_get, METH_FASTCALL, dict_get__doc__}, static PyObject * dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value); static PyObject * dict_get(PyDictObject *self, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; PyObject *key; PyObject *default_value = Py_None; if (!_PyArg_UnpackStack(args, nargs, "get", 1, 2, &key, &default_value)) { goto exit; } return_value = dict_get_impl(self, key, default_value); exit: return return_value; } PyDoc_STRVAR(dict___reversed____doc__, "__reversed__($self, /)\n" "--\n" "\n" "Return a reverse iterator over the dict keys."); #define DICT___REVERSED___METHODDEF \ {"__reversed__", (PyCFunction)dict___reversed__, METH_NOARGS, dict___reversed____doc__}, static PyObject * dict___reversed___impl(PyDictObject *self); static PyObject * dict___reversed__(PyDictObject *self, PyObject *Py_UNUSED(ignored)) { return dict___reversed___impl(self); } /*[clinic end generated code: output=7b77c16e43d6735a input=a9049054013a1b77]*/ python-frozendict/src/frozendict/c_src/3_7/cpython_src/Objects/dict-common.h000066400000000000000000000043361470250572200275030ustar00rootroot00000000000000#ifndef Py_DICT_COMMON_H #define Py_DICT_COMMON_H typedef struct { /* Cached hash code of me_key. */ Py_hash_t me_hash; PyObject *me_key; PyObject *me_value; /* This field is only meaningful for combined tables */ } PyDictKeyEntry; /* dict_lookup_func() returns index of entry which can be used like DK_ENTRIES(dk)[index]. * -1 when no entry found, -3 when compare raises error. */ typedef Py_ssize_t (*dict_lookup_func) (PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); #define DKIX_EMPTY (-1) #define DKIX_DUMMY (-2) /* Used internally */ #define DKIX_ERROR (-3) /* See dictobject.c for actual layout of DictKeysObject */ struct _dictkeysobject { Py_ssize_t dk_refcnt; /* Size of the hash table (dk_indices). It must be a power of 2. */ Py_ssize_t dk_size; /* Function to lookup in the hash table (dk_indices): - lookdict(): general-purpose, and may return DKIX_ERROR if (and only if) a comparison raises an exception. - lookdict_unicode(): specialized to Unicode string keys, comparison of which can never raise an exception; that function can never return DKIX_ERROR. - lookdict_unicode_nodummy(): similar to lookdict_unicode() but further specialized for Unicode string keys that cannot be the value. - lookdict_split(): Version of lookdict() for split tables. */ dict_lookup_func dk_lookup; /* Number of usable entries in dk_entries. */ Py_ssize_t dk_usable; /* Number of used entries in dk_entries. */ Py_ssize_t dk_nentries; /* Actual hash table of dk_size entries. It holds indices in dk_entries, or DKIX_EMPTY(-1) or DKIX_DUMMY(-2). Indices must be: 0 <= indice < USABLE_FRACTION(dk_size). The size in bytes of an indice depends on dk_size: - 1 byte if dk_size <= 0xff (char*) - 2 bytes if dk_size <= 0xffff (int16_t*) - 4 bytes if dk_size <= 0xffffffff (int32_t*) - 8 bytes otherwise (int64_t*) Dynamically sized, SIZEOF_VOID_P is minimum. */ char dk_indices[]; /* char is required to avoid strict aliasing. */ /* "PyDictKeyEntry dk_entries[dk_usable];" array follows: see the DK_ENTRIES() macro */ }; #endif python-frozendict/src/frozendict/c_src/3_7/cpython_src/Objects/dictobject.c000066400000000000000000001706121470250572200274000ustar00rootroot00000000000000/* Dictionary object implementation using a hash table */ /* The distribution includes a separate file, Objects/dictnotes.txt, describing explorations into dictionary design and optimization. It covers typical dictionary use patterns, the parameters for tuning dictionaries, and several ideas for possible optimizations. */ /* PyDictKeysObject This implements the dictionary's hashtable. As of Python 3.6, this is compact and ordered. Basic idea is described here: * https://mail.python.org/pipermail/python-dev/2012-December/123028.html * https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html layout: +---------------+ | dk_refcnt | | dk_size | | dk_lookup | | dk_usable | | dk_nentries | +---------------+ | dk_indices | | | +---------------+ | dk_entries | | | +---------------+ dk_indices is actual hashtable. It holds index in entries, or DKIX_EMPTY(-1) or DKIX_DUMMY(-2). Size of indices is dk_size. Type of each index in indices is vary on dk_size: * int8 for dk_size <= 128 * int16 for 256 <= dk_size <= 2**15 * int32 for 2**16 <= dk_size <= 2**31 * int64 for 2**32 <= dk_size dk_entries is array of PyDictKeyEntry. Its size is USABLE_FRACTION(dk_size). DK_ENTRIES(dk) can be used to get pointer to entries. NOTE: Since negative value is used for DKIX_EMPTY and DKIX_DUMMY, type of dk_indices entry is signed integer and int16 is used for table which dk_size == 256. */ /* The DictObject can be in one of two forms. Either: A combined table: ma_values == NULL, dk_refcnt == 1. Values are stored in the me_value field of the PyDictKeysObject. Or: A split table: ma_values != NULL, dk_refcnt >= 1 Values are stored in the ma_values array. Only string (unicode) keys are allowed. All dicts sharing same key must have same insertion order. There are four kinds of slots in the table (slot is index, and DK_ENTRIES(keys)[index] if index >= 0): 1. Unused. index == DKIX_EMPTY Does not hold an active (key, value) pair now and never did. Unused can transition to Active upon key insertion. This is each slot's initial state. 2. Active. index >= 0, me_key != NULL and me_value != NULL Holds an active (key, value) pair. Active can transition to Dummy or Pending upon key deletion (for combined and split tables respectively). This is the only case in which me_value != NULL. 3. Dummy. index == DKIX_DUMMY (combined only) Previously held an active (key, value) pair, but that was deleted and an active pair has not yet overwritten the slot. Dummy can transition to Active upon key insertion. Dummy slots cannot be made Unused again else the probe sequence in case of collision would have no way to know they were once active. 4. Pending. index >= 0, key != NULL, and value == NULL (split only) Not yet inserted in split-table. */ /* Preserving insertion order It's simple for combined table. Since dk_entries is mostly append only, we can get insertion order by just iterating dk_entries. One exception is .popitem(). It removes last item in dk_entries and decrement dk_nentries to achieve amortized O(1). Since there are DKIX_DUMMY remains in dk_indices, we can't increment dk_usable even though dk_nentries is decremented. In split table, inserting into pending entry is allowed only for dk_entries[ix] where ix == mp->ma_used. Inserting into other index and deleting item cause converting the dict to the combined table. */ /* PyDict_MINSIZE is the starting size for any new dict. * 8 allows dicts with no more than 5 active entries; experiments suggested * this suffices for the majority of dicts (consisting mostly of usually-small * dicts created to pass keyword arguments). * Making this 8, rather than 4 reduces the number of resizes for most * dictionaries, without any significant extra memory use. */ #define PyDict_MINSIZE 8 #include "Python.h" #include "dict-common.h" #include "stringlib/eq.h" // unicode_eq() /*[clinic input] class dict "PyDictObject *" "&PyDict_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=f157a5a0ce9589d6]*/ /* To ensure the lookup algorithm terminates, there must be at least one Unused slot (NULL key) in the table. To avoid slowing down lookups on a near-full table, we resize the table when it's USABLE_FRACTION (currently two-thirds) full. */ #define PERTURB_SHIFT 5 /* Major subtleties ahead: Most hash schemes depend on having a "good" hash function, in the sense of simulating randomness. Python doesn't: its most important hash functions (for ints) are very regular in common cases: >>>[hash(i) for i in range(4)] [0, 1, 2, 3] This isn't necessarily bad! To the contrary, in a table of size 2**i, taking the low-order i bits as the initial table index is extremely fast, and there are no collisions at all for dicts indexed by a contiguous range of ints. So this gives better-than-random behavior in common cases, and that's very desirable. OTOH, when collisions occur, the tendency to fill contiguous slices of the hash table makes a good collision resolution strategy crucial. Taking only the last i bits of the hash code is also vulnerable: for example, consider the list [i << 16 for i in range(20000)] as a set of keys. Since ints are their own hash codes, and this fits in a dict of size 2**15, the last 15 bits of every hash code are all 0: they *all* map to the same table index. But catering to unusual cases should not slow the usual ones, so we just take the last i bits anyway. It's up to collision resolution to do the rest. If we *usually* find the key we're looking for on the first try (and, it turns out, we usually do -- the table load factor is kept under 2/3, so the odds are solidly in our favor), then it makes best sense to keep the initial index computation dirt cheap. The first half of collision resolution is to visit table indices via this recurrence: j = ((5*j) + 1) mod 2**i For any initial j in range(2**i), repeating that 2**i times generates each int in range(2**i) exactly once (see any text on random-number generation for proof). By itself, this doesn't help much: like linear probing (setting j += 1, or j -= 1, on each loop trip), it scans the table entries in a fixed order. This would be bad, except that's not the only thing we do, and it's actually *good* in the common cases where hash keys are consecutive. In an example that's really too small to make this entirely clear, for a table of size 2**3 the order of indices is: 0 -> 1 -> 6 -> 7 -> 4 -> 5 -> 2 -> 3 -> 0 [and here it's repeating] If two things come in at index 5, the first place we look after is index 2, not 6, so if another comes in at index 6 the collision at 5 didn't hurt it. Linear probing is deadly in this case because there the fixed probe order is the *same* as the order consecutive keys are likely to arrive. But it's extremely unlikely hash codes will follow a 5*j+1 recurrence by accident, and certain that consecutive hash codes do not. The other half of the strategy is to get the other bits of the hash code into play. This is done by initializing a (unsigned) vrbl "perturb" to the full hash code, and changing the recurrence to: perturb >>= PERTURB_SHIFT; j = (5*j) + 1 + perturb; use j % 2**i as the next table index; Now the probe sequence depends (eventually) on every bit in the hash code, and the pseudo-scrambling property of recurring on 5*j+1 is more valuable, because it quickly magnifies small differences in the bits that didn't affect the initial index. Note that because perturb is unsigned, if the recurrence is executed often enough perturb eventually becomes and remains 0. At that point (very rarely reached) the recurrence is on (just) 5*j+1 again, and that's certain to find an empty slot eventually (since it generates every int in range(2**i), and we make sure there's always at least one empty slot). Selecting a good value for PERTURB_SHIFT is a balancing act. You want it small so that the high bits of the hash code continue to affect the probe sequence across iterations; but you want it large so that in really bad cases the high-order hash bits have an effect on early iterations. 5 was "the best" in minimizing total collisions across experiments Tim Peters ran (on both normal and pathological cases), but 4 and 6 weren't significantly worse. Historical: Reimer Behrends contributed the idea of using a polynomial-based approach, using repeated multiplication by x in GF(2**n) where an irreducible polynomial for each table size was chosen such that x was a primitive root. Christian Tismer later extended that to use division by x instead, as an efficient way to get the high bits of the hash code into play. This scheme also gave excellent collision statistics, but was more expensive: two if-tests were required inside the loop; computing "the next" index took about the same number of operations but without as much potential parallelism (e.g., computing 5*j can go on at the same time as computing 1+perturb in the above, and then shifting perturb can be done while the table index is being masked); and the PyDictObject struct required a member to hold the table's polynomial. In Tim's experiments the current scheme ran faster, produced equally good collision statistics, needed less code & used less memory. */ /* forward declarations */ static Py_ssize_t lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static Py_ssize_t lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); /*Global counter used to set ma_version_tag field of dictionary. * It is incremented each time that a dictionary is created and each * time that a dictionary is modified. */ static uint64_t pydict_global_version = 0; #define DICT_NEXT_VERSION() (++pydict_global_version) /* Dictionary reuse scheme to save calls to malloc and free */ #ifndef PyDict_MAXFREELIST #define PyDict_MAXFREELIST 80 #endif #if PyDict_MAXFREELIST > 0 static PyDictObject *free_list[PyDict_MAXFREELIST]; static int numfree = 0; static PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST]; static int numfreekeys = 0; #endif #include "clinic/dictobject.c.h" #define DK_SIZE(dk) ((dk)->dk_size) #if SIZEOF_VOID_P > 4 #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : DK_SIZE(dk) <= 0xffffffff ? \ 4 : sizeof(int64_t)) #else #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : sizeof(int32_t)) #endif #define DK_ENTRIES(dk) \ ((PyDictKeyEntry*)(&((int8_t*)((dk)->dk_indices))[DK_SIZE(dk) * DK_IXSIZE(dk)])) #define DK_MASK(dk) (((dk)->dk_size)-1) #define IS_POWER_OF_2(x) (((x) & (x-1)) == 0) static void free_keys_object(PyDictKeysObject *keys); static inline void dictkeys_incref(PyDictKeysObject *dk) { _Py_INC_REFTOTAL; dk->dk_refcnt++; } static inline void dictkeys_decref(PyDictKeysObject *dk) { assert(dk->dk_refcnt > 0); _Py_DEC_REFTOTAL; if (--dk->dk_refcnt == 0) { free_keys_object(dk); } } /* lookup indices. returns DKIX_EMPTY, DKIX_DUMMY, or ix >=0 */ static inline Py_ssize_t dictkeys_get_index(const PyDictKeysObject *keys, Py_ssize_t i) { Py_ssize_t s = DK_SIZE(keys); Py_ssize_t ix; if (s <= 0xff) { const int8_t *indices = (const int8_t*)(keys->dk_indices); ix = indices[i]; } else if (s <= 0xffff) { const int16_t *indices = (const int16_t*)(keys->dk_indices); ix = indices[i]; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { const int64_t *indices = (const int64_t*)(keys->dk_indices); ix = indices[i]; } #endif else { const int32_t *indices = (const int32_t*)(keys->dk_indices); ix = indices[i]; } assert(ix >= DKIX_DUMMY); return ix; } /* write to indices. */ static inline void dictkeys_set_index(PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix) { Py_ssize_t s = DK_SIZE(keys); assert(ix >= DKIX_DUMMY); if (s <= 0xff) { int8_t *indices = (int8_t*)(keys->dk_indices); assert(ix <= 0x7f); indices[i] = (char)ix; } else if (s <= 0xffff) { int16_t *indices = (int16_t*)(keys->dk_indices); assert(ix <= 0x7fff); indices[i] = (int16_t)ix; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { int64_t *indices = (int64_t*)(keys->dk_indices); indices[i] = ix; } #endif else { int32_t *indices = (int32_t*)(keys->dk_indices); assert(ix <= 0x7fffffff); indices[i] = (int32_t)ix; } } /* USABLE_FRACTION is the maximum dictionary load. * Increasing this ratio makes dictionaries more dense resulting in more * collisions. Decreasing it improves sparseness at the expense of spreading * indices over more cache lines and at the cost of total memory consumed. * * USABLE_FRACTION must obey the following: * (0 < USABLE_FRACTION(n) < n) for all n >= 2 * * USABLE_FRACTION should be quick to calculate. * Fractions around 1/2 to 2/3 seem to work well in practice. */ #define USABLE_FRACTION(n) (((n) << 1)/3) /* Find the smallest dk_size >= minsize. */ static inline Py_ssize_t calculate_keysize(Py_ssize_t minsize) { #if SIZEOF_LONG == SIZEOF_SIZE_T minsize = (minsize | PyDict_MINSIZE) - 1; return 1LL << _Py_bit_length(minsize | (PyDict_MINSIZE-1)); #elif defined(_MSC_VER) // On 64bit Windows, sizeof(long) == 4. minsize = (minsize | PyDict_MINSIZE) - 1; unsigned long msb; _BitScanReverse64(&msb, (uint64_t)minsize); return 1LL << (msb + 1); #else Py_ssize_t size; for (size = PyDict_MINSIZE; size < minsize && size > 0; size <<= 1) ; return size; #endif } /* estimate_keysize is reverse function of USABLE_FRACTION. * * This can be used to reserve enough size to insert n entries without * resizing. */ static inline Py_ssize_t estimate_keysize(Py_ssize_t n) { return calculate_keysize((n*3 + 1) / 2); } /* GROWTH_RATE. Growth rate upon hitting maximum load. * Currently set to used*3. * This means that dicts double in size when growing without deletions, * but have more head room when the number of deletions is on a par with the * number of insertions. See also bpo-17563 and bpo-33205. * * GROWTH_RATE was set to used*4 up to version 3.2. * GROWTH_RATE was set to used*2 in version 3.3.0 * GROWTH_RATE was set to used*2 + capacity/2 in 3.4.0-3.6.0. */ #define GROWTH_RATE(d) ((d)->ma_used*3) /* This immutable, empty PyDictKeysObject is used for PyDict_Clear() * (which cannot fail and thus can do no allocation). */ static PyDictKeysObject empty_keys_struct = { 1, /* dk_refcnt */ 1, /* dk_size */ lookdict_unicode_nodummy, /* dk_lookup */ 0, /* dk_usable (immutable) */ 0, /* dk_nentries */ {DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY}, /* dk_indices */ }; static PyObject *empty_values[1] = { NULL }; #define Py_EMPTY_KEYS &empty_keys_struct /* Uncomment to check the dict content in _PyDict_CheckConsistency() */ /* #define DEBUG_PYDICT */ #ifdef DEBUG_PYDICT # define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 1)) #else # define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 0)) #endif #ifndef NDEBUG static int _PyDict_CheckConsistency(PyObject *op, int check_content) { assert(op != NULL); assert(PyAnyDict_Check(op)); PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; int splitted = _PyDict_HasSplitTable(mp); Py_ssize_t usable = USABLE_FRACTION(keys->dk_size); assert(0 <= mp->ma_used && mp->ma_used <= usable); assert(IS_POWER_OF_2(keys->dk_size)); assert(0 <= keys->dk_usable && keys->dk_usable <= usable); assert(0 <= keys->dk_nentries && keys->dk_nentries <= usable); assert(keys->dk_usable + keys->dk_nentries <= usable); if (!splitted) { /* combined table */ assert(keys->dk_refcnt == 1); } if (check_content) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i; for (i=0; i < keys->dk_size; i++) { Py_ssize_t ix = dictkeys_get_index(keys, i); assert(DKIX_DUMMY <= ix && ix <= usable); } for (i=0; i < usable; i++) { PyDictKeyEntry *entry = &entries[i]; PyObject *key = entry->me_key; if (key != NULL) { if (PyUnicode_CheckExact(key)) { Py_hash_t hash = ((PyASCIIObject *)key)->hash; assert(hash != -1); assert(entry->me_hash == hash); } else { /* test_dict fails if PyObject_Hash() is called again */ assert(entry->me_hash != -1); } if (!splitted) { assert(entry->me_value != NULL); } } if (splitted) { assert(entry->me_value == NULL); } } if (splitted) { /* splitted table */ for (i=0; i < mp->ma_used; i++) { assert(mp->ma_values[i] != NULL); } } } return 1; } #endif static PyDictKeysObject* new_keys_object(Py_ssize_t size) { PyDictKeysObject *dk; Py_ssize_t es, usable; assert(size >= PyDict_MINSIZE); assert(IS_POWER_OF_2(size)); usable = USABLE_FRACTION(size); if (size <= 0xff) { es = 1; } else if (size <= 0xffff) { es = 2; } #if SIZEOF_VOID_P > 4 else if (size <= 0xffffffff) { es = 4; } #endif else { es = sizeof(Py_ssize_t); } #if PyDict_MAXFREELIST > 0 if (size == PyDict_MINSIZE && numfreekeys > 0) { dk = keys_free_list[--numfreekeys]; } else #endif { dk = PyObject_Malloc(sizeof(PyDictKeysObject) + es * size + sizeof(PyDictKeyEntry) * usable); if (dk == NULL) { PyErr_NoMemory(); return NULL; } } _Py_INC_REFTOTAL; dk->dk_refcnt = 1; dk->dk_size = size; dk->dk_usable = usable; dk->dk_lookup = lookdict_unicode_nodummy; dk->dk_nentries = 0; memset(&dk->dk_indices[0], 0xff, es * size); memset(DK_ENTRIES(dk), 0, sizeof(PyDictKeyEntry) * usable); return dk; } static void free_keys_object(PyDictKeysObject *keys) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n; for (i = 0, n = keys->dk_nentries; i < n; i++) { Py_XDECREF(entries[i].me_key); Py_XDECREF(entries[i].me_value); } #if PyDict_MAXFREELIST > 0 if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) { keys_free_list[numfreekeys++] = keys; return; } #endif PyObject_Free(keys); } #define new_values(size) PyMem_NEW(PyObject *, size) #define free_values(values) PyMem_Free(values) static Py_ssize_t _d_PyDict_KeysSize(PyDictKeysObject *keys) { return (sizeof(PyDictKeysObject) + DK_IXSIZE(keys) * DK_SIZE(keys) + USABLE_FRACTION(DK_SIZE(keys)) * sizeof(PyDictKeyEntry)); } static PyDictKeysObject * clone_combined_dict_keys(PyDictObject *orig) { assert(PyAnyDict_Check(orig)); assert( Py_TYPE(orig)->tp_iter == PyDict_Type.tp_iter || Py_TYPE(orig)->tp_iter == (getiterfunc)frozendict_iter ); assert(orig->ma_values == NULL); assert(orig->ma_keys->dk_refcnt == 1); Py_ssize_t keys_size = _d_PyDict_KeysSize(orig->ma_keys); PyDictKeysObject *keys = PyObject_Malloc(keys_size); if (keys == NULL) { PyErr_NoMemory(); return NULL; } memcpy(keys, orig->ma_keys, keys_size); /* After copying key/value pairs, we need to incref all keys and values and they are about to be co-owned by a new dict object. */ PyDictKeyEntry *ep0 = DK_ENTRIES(keys); Py_ssize_t n = keys->dk_nentries; for (Py_ssize_t i = 0; i < n; i++) { PyDictKeyEntry *entry = &ep0[i]; PyObject *value = entry->me_value; Py_INCREF(value); Py_INCREF(entry->me_key); } /* Since we copied the keys table we now have an extra reference in the system. Manually call _Py_INC_REFTOTAL to signal that we have it now; calling dictkeys_incref would be an error as keys->dk_refcnt is already set to 1 (after memcpy). */ _Py_INC_REFTOTAL; return keys; } /* The basic lookup function used by all operations. This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4. Open addressing is preferred over chaining since the link overhead for chaining would be substantial (100% with typical malloc overhead). The initial probe index is computed as hash mod the table size. Subsequent probe indices are computed as explained earlier. All arithmetic on hash should ignore overflow. The details in this version are due to Tim Peters, building on many past contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and Christian Tismer. lookdict() is general-purpose, and may return DKIX_ERROR if (and only if) a comparison raises an exception. lookdict_unicode() below is specialized to string keys, comparison of which can never raise an exception; that function can never return DKIX_ERROR when key is string. Otherwise, it falls back to lookdict(). lookdict_unicode_nodummy is further specialized for string keys that cannot be the value. For both, when the key isn't found a DKIX_EMPTY is returned. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { size_t i, mask, perturb; PyDictKeysObject *dk; PyDictKeyEntry *ep0; top: dk = mp->ma_keys; ep0 = DK_ENTRIES(dk); mask = DK_MASK(dk); perturb = hash; i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(dk, i); if (ix == DKIX_EMPTY) { *value_addr = NULL; return ix; } if (ix >= 0) { PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); if (ep->me_key == key) { *value_addr = ep->me_value; return ix; } if (ep->me_hash == hash) { PyObject *startkey = ep->me_key; Py_INCREF(startkey); int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); Py_DECREF(startkey); if (cmp < 0) { *value_addr = NULL; return DKIX_ERROR; } if (dk == mp->ma_keys && ep->me_key == startkey) { if (cmp > 0) { *value_addr = ep->me_value; return ix; } } else { /* The dict was mutated, restart */ goto top; } } } perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; } Py_UNREACHABLE(); } /* Faster version of lookdict_unicode when it is known that no keys * will be present. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { assert(mp->ma_values == NULL); /* Make sure this function doesn't have to handle non-unicode keys, including subclasses of str; e.g., one reason to subclass unicodes is to override __eq__, and for speed we don't cater to that here. */ if (!PyUnicode_CheckExact(key)) { return lookdict(mp, key, hash, value_addr); } PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); size_t mask = DK_MASK(mp->ma_keys); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i); assert (ix != DKIX_DUMMY); if (ix == DKIX_EMPTY) { *value_addr = NULL; return DKIX_EMPTY; } PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { *value_addr = ep->me_value; return ix; } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } #define MAINTAIN_TRACKING(mp, key, value) \ do { \ if (!_PyObject_GC_IS_TRACKED(mp)) { \ if (_PyObject_GC_MAY_BE_TRACKED(key) || \ _PyObject_GC_MAY_BE_TRACKED(value)) { \ PyObject_GC_Track(mp); \ } \ } \ } while(0) /* Internal function to find slot for an item from its hash when it is known that the key is not present in the dict. The dict must be combined. */ static Py_ssize_t find_empty_slot(PyDictKeysObject *keys, Py_hash_t hash) { assert(keys != NULL); const size_t mask = DK_MASK(keys); size_t i = hash & mask; Py_ssize_t ix = dictkeys_get_index(keys, i); for (size_t perturb = hash; ix >= 0;) { perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; ix = dictkeys_get_index(keys, i); } return i; } /* Internal routine used by dictresize() to build a hashtable of entries. */ static void build_indices(PyDictKeysObject *keys, PyDictKeyEntry *ep, Py_ssize_t n) { size_t mask = (size_t)DK_SIZE(keys) - 1; for (Py_ssize_t ix = 0; ix != n; ix++, ep++) { Py_hash_t hash = ep->me_hash; size_t i = hash & mask; for (size_t perturb = hash; dictkeys_get_index(keys, i) != DKIX_EMPTY;) { perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } dictkeys_set_index(keys, i, ix); } } static int _d_PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue, Py_hash_t *phash) { Py_ssize_t i; PyDictObject *mp; PyDictKeyEntry *entry_ptr; PyObject *value; if (!PyAnyDict_Check(op)) return 0; mp = (PyDictObject *)op; i = *ppos; if (mp->ma_values) { if (i < 0 || i >= mp->ma_used) return 0; /* values of split table is always dense */ entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; value = mp->ma_values[i]; assert(value != NULL); } else { Py_ssize_t n = mp->ma_keys->dk_nentries; if (i < 0 || i >= n) return 0; entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) return 0; value = entry_ptr->me_value; } *ppos = i+1; if (pkey) *pkey = entry_ptr->me_key; if (phash) *phash = entry_ptr->me_hash; if (pvalue) *pvalue = value; return 1; } /* Methods */ static void dict_dealloc(PyDictObject *mp) { PyObject **values = mp->ma_values; PyDictKeysObject *keys = mp->ma_keys; Py_ssize_t i, n; /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(mp); Py_TRASHCAN_SAFE_BEGIN(mp) if (values != NULL) { if (values != empty_values) { for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) { Py_XDECREF(values[i]); } free_values(values); } dictkeys_decref(keys); } else if (keys != NULL) { assert(keys->dk_refcnt == 1 || keys == Py_EMPTY_KEYS); dictkeys_decref(keys); } #if PyDict_MAXFREELIST > 0 if (numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type)) { free_list[numfree++] = mp; } else #endif { Py_TYPE(mp)->tp_free((PyObject *)mp); } Py_TRASHCAN_SAFE_END(mp) } static PyObject * dict_repr(PyDictObject *mp) { Py_ssize_t i; PyObject *key = NULL, *value = NULL; _PyUnicodeWriter writer; int first; i = Py_ReprEnter((PyObject *)mp); if (i != 0) { return i > 0 ? PyUnicode_FromString("{...}") : NULL; } if (mp->ma_used == 0) { Py_ReprLeave((PyObject *)mp); return PyUnicode_FromString("{}"); } _PyUnicodeWriter_Init(&writer); writer.overallocate = 1; /* "{" + "1: 2" + ", 3: 4" * (len - 1) + "}" */ writer.min_length = 1 + 4 + (2 + 4) * (mp->ma_used - 1) + 1; if (_PyUnicodeWriter_WriteChar(&writer, '{') < 0) goto error; /* Do repr() on each key+value pair, and insert ": " between them. Note that repr may mutate the dict. */ i = 0; first = 1; while (_d_PyDict_Next((PyObject *)mp, &i, &key, &value, NULL)) { PyObject *s; int res; /* Prevent repr from deleting key or value during key format. */ Py_INCREF(key); Py_INCREF(value); if (!first) { if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0) goto error; } first = 0; s = PyObject_Repr(key); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; if (_PyUnicodeWriter_WriteASCIIString(&writer, ": ", 2) < 0) goto error; s = PyObject_Repr(value); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; Py_CLEAR(key); Py_CLEAR(value); } writer.overallocate = 0; if (_PyUnicodeWriter_WriteChar(&writer, '}') < 0) goto error; Py_ReprLeave((PyObject *)mp); return _PyUnicodeWriter_Finish(&writer); error: Py_ReprLeave((PyObject *)mp); _PyUnicodeWriter_Dealloc(&writer); Py_XDECREF(key); Py_XDECREF(value); return NULL; } static Py_ssize_t dict_length(PyDictObject *mp) { return mp->ma_used; } static PyObject * dict_subscript(PyDictObject *mp, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || value == NULL) { if (!PyAnyDict_CheckExact(mp)) { /* Look up __missing__ method if we're a subclass. */ PyObject *missing, *res; _Py_IDENTIFIER(__missing__); missing = _PyObject_LookupSpecial((PyObject *)mp, &PyId___missing__); if (missing != NULL) { res = PyObject_CallFunctionObjArgs(missing, key, NULL); Py_DECREF(missing); return res; } else if (PyErr_Occurred()) return NULL; } _PyErr_SetKeyError(key); return NULL; } Py_INCREF(value); return value; } static PyObject * dict_richcompare(PyObject *v, PyObject *w, int op) { int cmp; PyObject *res; if (!PyAnyDict_Check(v) || !PyAnyDict_Check(w)) { res = Py_NotImplemented; } else if (op == Py_EQ || op == Py_NE) { cmp = frozendict_equal((PyDictObject *)v, (PyDictObject *)w); if (cmp < 0) return NULL; res = (cmp == (op == Py_EQ)) ? Py_True : Py_False; } else res = Py_NotImplemented; Py_INCREF(res); return res; } /*[clinic input] @coexist dict.__contains__ key: object / True if the dictionary has the specified key, else False. [clinic start generated code]*/ static PyObject * dict___contains__(PyDictObject *self, PyObject *key) /*[clinic end generated code: output=a3d03db709ed6e6b input=fe1cb42ad831e820]*/ { register PyDictObject *mp = self; Py_hash_t hash; Py_ssize_t ix; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || value == NULL) Py_RETURN_FALSE; Py_RETURN_TRUE; } /*[clinic input] dict.get key: object default: object = None / Return the value for key if key is in the dictionary, else default. [clinic start generated code]*/ static PyObject * dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value) /*[clinic end generated code: output=bba707729dee05bf input=279ddb5790b6b107]*/ { PyObject *val = NULL; Py_hash_t hash; Py_ssize_t ix; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (self->ma_keys->dk_lookup) (self, key, hash, &val); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || val == NULL) { val = default_value; } Py_INCREF(val); return val; } static int dict_traverse(PyObject *op, visitproc visit, void *arg) { PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n = keys->dk_nentries; if (keys->dk_lookup == lookdict) { for (i = 0; i < n; i++) { if (entries[i].me_value != NULL) { Py_VISIT(entries[i].me_value); Py_VISIT(entries[i].me_key); } } } else { if (mp->ma_values != NULL) { for (i = 0; i < n; i++) { Py_VISIT(mp->ma_values[i]); } } else { for (i = 0; i < n; i++) { Py_VISIT(entries[i].me_value); } } } return 0; } static PyObject *dictiter_new(PyDictObject *, PyTypeObject *); static PyObject * dict_sizeof(PyDictObject *mp, PyObject *Py_UNUSED(ignored)) { return PyLong_FromSsize_t(_PyDict_SizeOf(mp)); } PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]"); PyDoc_STRVAR(sizeof__doc__, "D.__sizeof__() -> size of D in memory, in bytes"); PyDoc_STRVAR(copy__doc__, "D.copy() -> a shallow copy of D"); PyDoc_STRVAR(keys__doc__, "D.keys() -> a set-like object providing a view on D's keys"); PyDoc_STRVAR(items__doc__, "D.items() -> a set-like object providing a view on D's items"); PyDoc_STRVAR(values__doc__, "D.values() -> an object providing a view on D's values"); /* Hack to implement "key in dict" */ static PySequenceMethods dict_as_sequence = { 0, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ PyDict_Contains, /* sq_contains */ 0, /* sq_inplace_concat */ 0, /* sq_inplace_repeat */ }; /* Dictionary iterator types */ typedef struct { PyObject_HEAD PyDictObject *di_dict; /* Set to NULL when iterator is exhausted */ Py_ssize_t di_used; Py_ssize_t di_pos; PyObject* di_result; /* reusable result tuple for iteritems */ Py_ssize_t len; } dictiterobject; PyTypeObject PyDictRevIterKey_Type; PyTypeObject PyDictRevIterValue_Type; PyTypeObject PyDictRevIterItem_Type; static PyObject * dictiter_new(PyDictObject *dict, PyTypeObject *itertype) { dictiterobject *di; di = PyObject_GC_New(dictiterobject, itertype); if (di == NULL) { return NULL; } Py_INCREF(dict); di->di_dict = dict; di->di_used = dict->ma_used; di->len = dict->ma_used; if (itertype == &PyDictRevIterKey_Type || itertype == &PyDictRevIterItem_Type || itertype == &PyDictRevIterValue_Type) { if (dict->ma_values) { di->di_pos = dict->ma_used - 1; } else { di->di_pos = dict->ma_keys->dk_nentries - 1; } } else { di->di_pos = 0; } if ( itertype == &PyFrozenDictIterItem_Type || itertype == &PyDictRevIterItem_Type ) { di->di_result = PyTuple_Pack(2, Py_None, Py_None); if (di->di_result == NULL) { Py_DECREF(di); return NULL; } } else { di->di_result = NULL; } PyObject_GC_Track(di); return (PyObject *)di; } static void dictiter_dealloc(dictiterobject *di) { /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(di); Py_XDECREF(di->di_dict); Py_XDECREF(di->di_result); PyObject_GC_Del(di); } static int dictiter_traverse(dictiterobject *di, visitproc visit, void *arg) { Py_VISIT(di->di_dict); Py_VISIT(di->di_result); return 0; } static PyObject * dictiter_len(dictiterobject *di, PyObject *Py_UNUSED(ignored)) { Py_ssize_t len = 0; if (di->di_dict != NULL && di->di_used == di->di_dict->ma_used) len = di->len; return PyLong_FromSize_t(len); } PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); static PyObject * dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyMethodDef dictiter_methods[] = { {"__length_hint__", (PyCFunction)(void(*)(void))dictiter_len, METH_NOARGS, length_hint_doc}, {"__reduce__", (PyCFunction)(void(*)(void))dictiter_reduce, METH_NOARGS, reduce_doc}, {NULL, NULL} /* sentinel */ }; /* dictreviter */ static PyObject * dictreviter_iternext(dictiterobject *di) { PyDictObject *d = di->di_dict; if (d == NULL) { return NULL; } assert (PyAnyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } Py_ssize_t i = di->di_pos; PyDictKeysObject *k = d->ma_keys; PyObject *key, *value, *result; if (i < 0) { goto fail; } if (d->ma_values) { key = DK_ENTRIES(k)[i].me_key; value = d->ma_values[i]; assert (value != NULL); } else { PyDictKeyEntry *entry_ptr = &DK_ENTRIES(k)[i]; while (entry_ptr->me_value == NULL) { if (--i < 0) { goto fail; } entry_ptr--; } key = entry_ptr->me_key; value = entry_ptr->me_value; } di->di_pos = i-1; di->len--; if (Py_IS_TYPE(di, &PyDictRevIterKey_Type)) { Py_INCREF(key); return key; } else if (Py_IS_TYPE(di, &PyDictRevIterValue_Type)) { Py_INCREF(value); return value; } else if (Py_IS_TYPE(di, &PyDictRevIterItem_Type)) { Py_INCREF(key); Py_INCREF(value); result = di->di_result; if (Py_REFCNT(result) == 1) { PyObject *oldkey = PyTuple_GET_ITEM(result, 0); PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); // bpo-42536: The GC may have untracked this result tuple. Since // we're recycling it, make sure it's tracked again: // if (!_PyObject_GC_IS_TRACKED(result)) { // _PyObject_GC_TRACK(result); // } } else { result = PyTuple_New(2); if (result == NULL) { return NULL; } PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ } return result; } else { Py_UNREACHABLE(); } fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictRevIterKey_Type = { PyVarObject_HEAD_INIT(NULL, 0) "dict_reversekeyiterator", sizeof(dictiterobject), .tp_dealloc = (destructor)dictiter_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_traverse = (traverseproc)dictiter_traverse, .tp_iter = PyObject_SelfIter, .tp_iternext = (iternextfunc)dictreviter_iternext, .tp_methods = dictiter_methods }; PyTypeObject PyDictRevIterItem_Type = { PyVarObject_HEAD_INIT(NULL, 0) "dict_reverseitemiterator", sizeof(dictiterobject), .tp_dealloc = (destructor)dictiter_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_traverse = (traverseproc)dictiter_traverse, .tp_iter = PyObject_SelfIter, .tp_iternext = (iternextfunc)dictreviter_iternext, .tp_methods = dictiter_methods }; PyTypeObject PyDictRevIterValue_Type = { PyVarObject_HEAD_INIT(NULL, 0) "dict_reversevalueiterator", sizeof(dictiterobject), .tp_dealloc = (destructor)dictiter_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_traverse = (traverseproc)dictiter_traverse, .tp_iter = PyObject_SelfIter, .tp_iternext = (iternextfunc)dictreviter_iternext, .tp_methods = dictiter_methods }; /*[clinic input] dict.__reversed__ Return a reverse iterator over the dict keys. [clinic start generated code]*/ static PyObject * dict___reversed___impl(PyDictObject *self) /*[clinic end generated code: output=e674483336d1ed51 input=23210ef3477d8c4d]*/ { assert (PyAnyDict_Check(self)); return dictiter_new(self, &PyDictRevIterKey_Type); } static PyObject * dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)) { _Py_IDENTIFIER(iter); /* copy the iterator state */ dictiterobject tmp = *di; Py_XINCREF(tmp.di_dict); PyObject *list = PySequence_List((PyObject*)&tmp); Py_XDECREF(tmp.di_dict); if (list == NULL) { return NULL; } return Py_BuildValue("N(N)", _PyEval_GetBuiltinId(&PyId_iter), list); } /***********************************************/ /* View objects for keys(), items(), values(). */ /***********************************************/ /* The instance lay-out is the same for all three; but the type differs. */ static void dictview_dealloc(_PyDictViewObject *dv) { /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(dv); Py_XDECREF(dv->dv_dict); PyObject_GC_Del(dv); } static int dictview_traverse(_PyDictViewObject *dv, visitproc visit, void *arg) { Py_VISIT(dv->dv_dict); return 0; } static Py_ssize_t dictview_len(_PyDictViewObject *dv) { Py_ssize_t len = 0; if (dv->dv_dict != NULL) len = dv->dv_dict->ma_used; return len; } static PyObject * _d_PyDictView_New(PyObject *dict, PyTypeObject *type) { _PyDictViewObject *dv; if (dict == NULL) { PyErr_BadInternalCall(); return NULL; } if (!PyAnyDict_Check(dict)) { /* XXX Get rid of this restriction later */ PyErr_Format(PyExc_TypeError, "%s() requires a dict argument, not '%s'", type->tp_name, Py_TYPE(dict)->tp_name); return NULL; } dv = PyObject_GC_New(_PyDictViewObject, type); if (dv == NULL) return NULL; Py_INCREF(dict); dv->dv_dict = (PyDictObject *)dict; PyObject_GC_Track(dv); return (PyObject *)dv; } static PyObject * dictview_mapping(PyObject *view, void *Py_UNUSED(ignored)) { assert(view != NULL); assert(PyAnyDictKeys_Check(view) || PyAnyDictValues_Check(view) || PyAnyDictItems_Check(view)); PyObject *mapping = (PyObject *)((_PyDictViewObject *)view)->dv_dict; return PyDictProxy_New(mapping); } static PyGetSetDef dictview_getset[] = { {"mapping", dictview_mapping, (setter)NULL, "dictionary that this view refers to", NULL}, {0} }; /* TODO(guido): The views objects are not complete: * support more set operations * support arbitrary mappings? - either these should be static or exported in dictobject.h - if public then they should probably be in builtins */ /* Return 1 if self is a subset of other, iterating over self; 0 if not; -1 if an error occurred. */ static int all_contained_in(PyObject *self, PyObject *other) { PyObject *iter = PyObject_GetIter(self); int ok = 1; if (iter == NULL) return -1; for (;;) { PyObject *next = PyIter_Next(iter); if (next == NULL) { if (PyErr_Occurred()) ok = -1; break; } ok = PySequence_Contains(other, next); Py_DECREF(next); if (ok <= 0) break; } Py_DECREF(iter); return ok; } static PyObject * dictview_richcompare(PyObject *self, PyObject *other, int op) { Py_ssize_t len_self, len_other; int ok; PyObject *result; assert(self != NULL); assert(PyAnyDictViewSet_Check(self)); assert(other != NULL); if (!PyAnySet_Check(other) && !PyAnyDictViewSet_Check(other)) Py_RETURN_NOTIMPLEMENTED; len_self = PyObject_Size(self); if (len_self < 0) return NULL; len_other = PyObject_Size(other); if (len_other < 0) return NULL; ok = 0; switch(op) { case Py_NE: case Py_EQ: if (len_self == len_other) ok = all_contained_in(self, other); if (op == Py_NE && ok >= 0) ok = !ok; break; case Py_LT: if (len_self < len_other) ok = all_contained_in(self, other); break; case Py_LE: if (len_self <= len_other) ok = all_contained_in(self, other); break; case Py_GT: if (len_self > len_other) ok = all_contained_in(other, self); break; case Py_GE: if (len_self >= len_other) ok = all_contained_in(other, self); break; } if (ok < 0) return NULL; result = ok ? Py_True : Py_False; Py_INCREF(result); return result; } static PyObject * dictview_repr(_PyDictViewObject *dv) { PyObject *seq; PyObject *result = NULL; Py_ssize_t rc; rc = Py_ReprEnter((PyObject *)dv); if (rc != 0) { return rc > 0 ? PyUnicode_FromString("...") : NULL; } seq = PySequence_List((PyObject *)dv); if (seq == NULL) { goto Done; } result = PyUnicode_FromFormat("%s(%R)", Py_TYPE(dv)->tp_name, seq); Py_DECREF(seq); Done: Py_ReprLeave((PyObject *)dv); return result; } /*** dict_keys ***/ static int dictkeys_contains(_PyDictViewObject *dv, PyObject *obj) { if (dv->dv_dict == NULL) return 0; return PyDict_Contains((PyObject *)dv->dv_dict, obj); } static PySequenceMethods dictkeys_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictkeys_contains, /* sq_contains */ }; // Create an set object from dictviews object. // Returns a new reference. // This utility function is used by set operations. static PyObject* dictviews_to_set(PyObject *self) { PyObject *left = self; if (PyAnyDictKeys_Check(self)) { // PySet_New() has fast path for the dict object. PyObject *dict = (PyObject *)((_PyDictViewObject *)self)->dv_dict; if (PyAnyDict_CheckExact(dict)) { left = dict; } } return PySet_New(left); } static PyObject* dictviews_sub(PyObject* self, PyObject *other) { PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } _Py_IDENTIFIER(difference_update); PyObject *tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_difference_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static int dictitems_contains(_PyDictViewObject *dv, PyObject *obj); static PyObject* _d_PyDictView_Intersect(PyObject* self, PyObject *other) { PyObject *result; PyObject *it; PyObject *key; Py_ssize_t len_self; int rv; int (*dict_contains)(_PyDictViewObject *, PyObject *); /* Python interpreter swaps parameters when dict view is on right side of & */ if (!PyAnyDictViewSet_Check(self)) { PyObject *tmp = other; other = self; self = tmp; } len_self = dictview_len((_PyDictViewObject *)self); /* if other is a set and self is smaller than other, reuse set intersection logic */ if (PySet_CheckExact(other) && len_self <= PyObject_Size(other)) { _Py_IDENTIFIER(intersection); return _PyObject_CallMethodIdObjArgs(other, &PyId_intersection, self, NULL); } /* if other is another dict view, and it is bigger than self, swap them */ if (PyAnyDictViewSet_Check(other)) { Py_ssize_t len_other = dictview_len((_PyDictViewObject *)other); if (len_other > len_self) { PyObject *tmp = other; other = self; self = tmp; } } /* at this point, two things should be true 1. self is a dictview 2. if other is a dictview then it is smaller than self */ result = PySet_New(NULL); if (result == NULL) return NULL; it = PyObject_GetIter(other); if (it == NULL) { Py_DECREF(result); return NULL; } if (PyAnyDictKeys_Check(self)) { dict_contains = dictkeys_contains; } /* else PyDictItems_Check(self) */ else { dict_contains = dictitems_contains; } while ((key = PyIter_Next(it)) != NULL) { rv = dict_contains((_PyDictViewObject *)self, key); if (rv < 0) { goto error; } if (rv) { if (PySet_Add(result, key)) { goto error; } } Py_DECREF(key); } Py_DECREF(it); if (PyErr_Occurred()) { Py_DECREF(result); return NULL; } return result; error: Py_DECREF(it); Py_DECREF(result); Py_DECREF(key); return NULL; } static PyObject* dictviews_or(PyObject* self, PyObject *other) { PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } if (_PySet_Update(result, other) < 0) { Py_DECREF(result); return NULL; } return result; } static PyObject * dictitems_xor(PyObject *self, PyObject *other) { assert(PyAnyDictItems_Check(self)); assert(PyAnyDictItems_Check(other)); PyObject *d1 = (PyObject *)((_PyDictViewObject *)self)->dv_dict; PyObject *d2 = (PyObject *)((_PyDictViewObject *)other)->dv_dict; PyObject *temp_dict; if (PyDict_CheckExact(d1)) { temp_dict = PyDict_Copy(d1); } else { PyObject* args = PyTuple_New(1); if (args == NULL) { return NULL; } Py_INCREF(d1); PyTuple_SET_ITEM(args, 0, d1); temp_dict = PyObject_Call((PyObject *) &PyDict_Type, args, NULL); Py_DECREF(args); } if (temp_dict == NULL) { return NULL; } PyObject *result_set = PySet_New(NULL); if (result_set == NULL) { Py_CLEAR(temp_dict); return NULL; } PyObject *key = NULL, *val1 = NULL, *val2 = NULL; Py_ssize_t pos = 0; Py_hash_t hash; while (_d_PyDict_Next(d2, &pos, &key, &val2, &hash)) { Py_INCREF(key); Py_INCREF(val2); val1 = _PyDict_GetItem_KnownHash(temp_dict, key, hash); int to_delete; if (val1 == NULL) { if (PyErr_Occurred()) { goto error; } to_delete = 0; } else { Py_INCREF(val1); to_delete = PyObject_RichCompareBool(val1, val2, Py_EQ); if (to_delete < 0) { goto error; } } if (to_delete) { if (_PyDict_DelItem_KnownHash(temp_dict, key, hash) < 0) { goto error; } } else { PyObject *pair = PyTuple_Pack(2, key, val2); if (pair == NULL) { goto error; } if (PySet_Add(result_set, pair) < 0) { Py_DECREF(pair); goto error; } Py_DECREF(pair); } Py_DECREF(key); Py_XDECREF(val1); Py_DECREF(val2); } key = val1 = val2 = NULL; _Py_IDENTIFIER(items); PyObject *remaining_pairs = _PyObject_CallMethodIdObjArgs(temp_dict, &PyId_items, NULL); if (remaining_pairs == NULL) { goto error; } if (_PySet_Update(result_set, remaining_pairs) < 0) { Py_DECREF(remaining_pairs); goto error; } Py_DECREF(temp_dict); Py_DECREF(remaining_pairs); return result_set; error: Py_XDECREF(temp_dict); Py_XDECREF(result_set); Py_XDECREF(key); Py_XDECREF(val1); Py_XDECREF(val2); return NULL; } static PyObject* dictviews_xor(PyObject* self, PyObject *other) { if (PyAnyDictItems_Check(self) && PyAnyDictItems_Check(other)) { return dictitems_xor(self, other); } PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } _Py_IDENTIFIER(symmetric_difference_update); PyObject *tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_symmetric_difference_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static PyNumberMethods dictviews_as_number = { 0, /*nb_add*/ (binaryfunc)dictviews_sub, /*nb_subtract*/ 0, /*nb_multiply*/ 0, /*nb_remainder*/ 0, /*nb_divmod*/ 0, /*nb_power*/ 0, /*nb_negative*/ 0, /*nb_positive*/ 0, /*nb_absolute*/ 0, /*nb_bool*/ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ (binaryfunc)_d_PyDictView_Intersect,/*nb_and*/ (binaryfunc)dictviews_xor, /*nb_xor*/ (binaryfunc)dictviews_or, /*nb_or*/ }; static PyObject* dictviews_isdisjoint(PyObject *self, PyObject *other) { PyObject *it; PyObject *item = NULL; if (self == other) { if (dictview_len((_PyDictViewObject *)self) == 0) Py_RETURN_TRUE; else Py_RETURN_FALSE; } /* Iterate over the shorter object (only if other is a set, * because PySequence_Contains may be expensive otherwise): */ if (PyAnySet_Check(other) || PyAnyDictViewSet_Check(other)) { Py_ssize_t len_self = dictview_len((_PyDictViewObject *)self); Py_ssize_t len_other = PyObject_Size(other); if (len_other == -1) return NULL; if ((len_other > len_self)) { PyObject *tmp = other; other = self; self = tmp; } } it = PyObject_GetIter(other); if (it == NULL) return NULL; while ((item = PyIter_Next(it)) != NULL) { int contains = PySequence_Contains(self, item); Py_DECREF(item); if (contains == -1) { Py_DECREF(it); return NULL; } if (contains) { Py_DECREF(it); Py_RETURN_FALSE; } } Py_DECREF(it); if (PyErr_Occurred()) return NULL; /* PyIter_Next raised an exception. */ Py_RETURN_TRUE; } PyDoc_STRVAR(isdisjoint_doc, "Return True if the view and the given iterable have a null intersection."); static PyObject* dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_keys_doc, "Return a reverse iterator over the dict keys."); static PyMethodDef dictkeys_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {"__reversed__", (PyCFunction)(void(*)(void))dictkeys_reversed, METH_NOARGS, reversed_keys_doc}, {NULL, NULL} /* sentinel */ }; static PyObject * dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterKey_Type); } /*** dict_items ***/ /* Variant of PyDict_GetItem() that doesn't suppress exceptions. This returns NULL *with* an exception set if an exception occurred. It returns NULL *without* an exception set if the key wasn't present. */ static PyObject * d_PyDict_GetItemWithError(PyObject *op, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyDictObject*mp = (PyDictObject *)op; PyObject *value; if (!PyAnyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { return NULL; } } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix < 0) return NULL; return value; } static int dictitems_contains(_PyDictViewObject *dv, PyObject *obj) { int result; PyObject *key, *value, *found; if (dv->dv_dict == NULL) return 0; if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) return 0; key = PyTuple_GET_ITEM(obj, 0); value = PyTuple_GET_ITEM(obj, 1); found = d_PyDict_GetItemWithError((PyObject *)dv->dv_dict, key); if (found == NULL) { if (PyErr_Occurred()) return -1; return 0; } Py_INCREF(found); result = PyObject_RichCompareBool(found, value, Py_EQ); Py_DECREF(found); return result; } static PySequenceMethods dictitems_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictitems_contains, /* sq_contains */ }; static PyObject* dictitems_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_items_doc, "Return a reverse iterator over the dict items."); static PyMethodDef dictitems_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {"__reversed__", (PyCFunction)dictitems_reversed, METH_NOARGS, reversed_items_doc}, {NULL, NULL} /* sentinel */ }; static PyObject * dictitems_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterItem_Type); } /*** dict_values ***/ static PySequenceMethods dictvalues_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)0, /* sq_contains */ }; static PyObject* dictvalues_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_values_doc, "Return a reverse iterator over the dict values."); static PyMethodDef dictvalues_methods[] = { {"__reversed__", (PyCFunction)dictvalues_reversed, METH_NOARGS, reversed_values_doc}, {NULL, NULL} /* sentinel */ }; static PyObject * dictvalues_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterValue_Type); } python-frozendict/src/frozendict/c_src/3_7/cpython_src/Objects/dictobject_original.c000066400000000000000000004022041470250572200312570ustar00rootroot00000000000000/* Dictionary object implementation using a hash table */ /* The distribution includes a separate file, Objects/dictnotes.txt, describing explorations into dictionary design and optimization. It covers typical dictionary use patterns, the parameters for tuning dictionaries, and several ideas for possible optimizations. */ /* PyDictKeysObject This implements the dictionary's hashtable. As of Python 3.6, this is compact and ordered. Basic idea is described here: * https://mail.python.org/pipermail/python-dev/2012-December/123028.html * https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html layout: +---------------+ | dk_refcnt | | dk_size | | dk_lookup | | dk_usable | | dk_nentries | +---------------+ | dk_indices | | | +---------------+ | dk_entries | | | +---------------+ dk_indices is actual hashtable. It holds index in entries, or DKIX_EMPTY(-1) or DKIX_DUMMY(-2). Size of indices is dk_size. Type of each index in indices is vary on dk_size: * int8 for dk_size <= 128 * int16 for 256 <= dk_size <= 2**15 * int32 for 2**16 <= dk_size <= 2**31 * int64 for 2**32 <= dk_size dk_entries is array of PyDictKeyEntry. Its size is USABLE_FRACTION(dk_size). DK_ENTRIES(dk) can be used to get pointer to entries. NOTE: Since negative value is used for DKIX_EMPTY and DKIX_DUMMY, type of dk_indices entry is signed integer and int16 is used for table which dk_size == 256. */ /* The DictObject can be in one of two forms. Either: A combined table: ma_values == NULL, dk_refcnt == 1. Values are stored in the me_value field of the PyDictKeysObject. Or: A split table: ma_values != NULL, dk_refcnt >= 1 Values are stored in the ma_values array. Only string (unicode) keys are allowed. All dicts sharing same key must have same insertion order. There are four kinds of slots in the table (slot is index, and DK_ENTRIES(keys)[index] if index >= 0): 1. Unused. index == DKIX_EMPTY Does not hold an active (key, value) pair now and never did. Unused can transition to Active upon key insertion. This is each slot's initial state. 2. Active. index >= 0, me_key != NULL and me_value != NULL Holds an active (key, value) pair. Active can transition to Dummy or Pending upon key deletion (for combined and split tables respectively). This is the only case in which me_value != NULL. 3. Dummy. index == DKIX_DUMMY (combined only) Previously held an active (key, value) pair, but that was deleted and an active pair has not yet overwritten the slot. Dummy can transition to Active upon key insertion. Dummy slots cannot be made Unused again else the probe sequence in case of collision would have no way to know they were once active. 4. Pending. index >= 0, key != NULL, and value == NULL (split only) Not yet inserted in split-table. */ /* Preserving insertion order It's simple for combined table. Since dk_entries is mostly append only, we can get insertion order by just iterating dk_entries. One exception is .popitem(). It removes last item in dk_entries and decrement dk_nentries to achieve amortized O(1). Since there are DKIX_DUMMY remains in dk_indices, we can't increment dk_usable even though dk_nentries is decremented. In split table, inserting into pending entry is allowed only for dk_entries[ix] where ix == mp->ma_used. Inserting into other index and deleting item cause converting the dict to the combined table. */ /* PyDict_MINSIZE is the starting size for any new dict. * 8 allows dicts with no more than 5 active entries; experiments suggested * this suffices for the majority of dicts (consisting mostly of usually-small * dicts created to pass keyword arguments). * Making this 8, rather than 4 reduces the number of resizes for most * dictionaries, without any significant extra memory use. */ #define PyDict_MINSIZE 8 #include "Python.h" #include "internal/pystate.h" #include "dict-common.h" #include "stringlib/eq.h" /* to get unicode_eq() */ /*[clinic input] class dict "PyDictObject *" "&PyDict_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=f157a5a0ce9589d6]*/ /* To ensure the lookup algorithm terminates, there must be at least one Unused slot (NULL key) in the table. To avoid slowing down lookups on a near-full table, we resize the table when it's USABLE_FRACTION (currently two-thirds) full. */ #define PERTURB_SHIFT 5 /* Major subtleties ahead: Most hash schemes depend on having a "good" hash function, in the sense of simulating randomness. Python doesn't: its most important hash functions (for ints) are very regular in common cases: >>>[hash(i) for i in range(4)] [0, 1, 2, 3] This isn't necessarily bad! To the contrary, in a table of size 2**i, taking the low-order i bits as the initial table index is extremely fast, and there are no collisions at all for dicts indexed by a contiguous range of ints. So this gives better-than-random behavior in common cases, and that's very desirable. OTOH, when collisions occur, the tendency to fill contiguous slices of the hash table makes a good collision resolution strategy crucial. Taking only the last i bits of the hash code is also vulnerable: for example, consider the list [i << 16 for i in range(20000)] as a set of keys. Since ints are their own hash codes, and this fits in a dict of size 2**15, the last 15 bits of every hash code are all 0: they *all* map to the same table index. But catering to unusual cases should not slow the usual ones, so we just take the last i bits anyway. It's up to collision resolution to do the rest. If we *usually* find the key we're looking for on the first try (and, it turns out, we usually do -- the table load factor is kept under 2/3, so the odds are solidly in our favor), then it makes best sense to keep the initial index computation dirt cheap. The first half of collision resolution is to visit table indices via this recurrence: j = ((5*j) + 1) mod 2**i For any initial j in range(2**i), repeating that 2**i times generates each int in range(2**i) exactly once (see any text on random-number generation for proof). By itself, this doesn't help much: like linear probing (setting j += 1, or j -= 1, on each loop trip), it scans the table entries in a fixed order. This would be bad, except that's not the only thing we do, and it's actually *good* in the common cases where hash keys are consecutive. In an example that's really too small to make this entirely clear, for a table of size 2**3 the order of indices is: 0 -> 1 -> 6 -> 7 -> 4 -> 5 -> 2 -> 3 -> 0 [and here it's repeating] If two things come in at index 5, the first place we look after is index 2, not 6, so if another comes in at index 6 the collision at 5 didn't hurt it. Linear probing is deadly in this case because there the fixed probe order is the *same* as the order consecutive keys are likely to arrive. But it's extremely unlikely hash codes will follow a 5*j+1 recurrence by accident, and certain that consecutive hash codes do not. The other half of the strategy is to get the other bits of the hash code into play. This is done by initializing a (unsigned) vrbl "perturb" to the full hash code, and changing the recurrence to: perturb >>= PERTURB_SHIFT; j = (5*j) + 1 + perturb; use j % 2**i as the next table index; Now the probe sequence depends (eventually) on every bit in the hash code, and the pseudo-scrambling property of recurring on 5*j+1 is more valuable, because it quickly magnifies small differences in the bits that didn't affect the initial index. Note that because perturb is unsigned, if the recurrence is executed often enough perturb eventually becomes and remains 0. At that point (very rarely reached) the recurrence is on (just) 5*j+1 again, and that's certain to find an empty slot eventually (since it generates every int in range(2**i), and we make sure there's always at least one empty slot). Selecting a good value for PERTURB_SHIFT is a balancing act. You want it small so that the high bits of the hash code continue to affect the probe sequence across iterations; but you want it large so that in really bad cases the high-order hash bits have an effect on early iterations. 5 was "the best" in minimizing total collisions across experiments Tim Peters ran (on both normal and pathological cases), but 4 and 6 weren't significantly worse. Historical: Reimer Behrends contributed the idea of using a polynomial-based approach, using repeated multiplication by x in GF(2**n) where an irreducible polynomial for each table size was chosen such that x was a primitive root. Christian Tismer later extended that to use division by x instead, as an efficient way to get the high bits of the hash code into play. This scheme also gave excellent collision statistics, but was more expensive: two if-tests were required inside the loop; computing "the next" index took about the same number of operations but without as much potential parallelism (e.g., computing 5*j can go on at the same time as computing 1+perturb in the above, and then shifting perturb can be done while the table index is being masked); and the PyDictObject struct required a member to hold the table's polynomial. In Tim's experiments the current scheme ran faster, produced equally good collision statistics, needed less code & used less memory. */ /* forward declarations */ static Py_ssize_t lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static Py_ssize_t lookdict_unicode(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static Py_ssize_t lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static Py_ssize_t lookdict_split(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static int dictresize(PyDictObject *mp, Py_ssize_t minused); static PyObject* dict_iter(PyDictObject *dict); /*Global counter used to set ma_version_tag field of dictionary. * It is incremented each time that a dictionary is created and each * time that a dictionary is modified. */ static uint64_t pydict_global_version = 0; #define DICT_NEXT_VERSION() (++pydict_global_version) /* Dictionary reuse scheme to save calls to malloc and free */ #ifndef PyDict_MAXFREELIST #define PyDict_MAXFREELIST 80 #endif static PyDictObject *free_list[PyDict_MAXFREELIST]; static int numfree = 0; static PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST]; static int numfreekeys = 0; #include "clinic/dictobject.c.h" int PyDict_ClearFreeList(void) { PyDictObject *op; int ret = numfree + numfreekeys; while (numfree) { op = free_list[--numfree]; assert(PyDict_CheckExact(op)); PyObject_GC_Del(op); } while (numfreekeys) { PyObject_FREE(keys_free_list[--numfreekeys]); } return ret; } /* Print summary info about the state of the optimized allocator */ void _PyDict_DebugMallocStats(FILE *out) { _PyDebugAllocatorStats(out, "free PyDictObject", numfree, sizeof(PyDictObject)); } void PyDict_Fini(void) { PyDict_ClearFreeList(); } #define DK_SIZE(dk) ((dk)->dk_size) #if SIZEOF_VOID_P > 4 #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : DK_SIZE(dk) <= 0xffffffff ? \ 4 : sizeof(int64_t)) #else #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : sizeof(int32_t)) #endif #define DK_ENTRIES(dk) \ ((PyDictKeyEntry*)(&((int8_t*)((dk)->dk_indices))[DK_SIZE(dk) * DK_IXSIZE(dk)])) #define DK_DEBUG_INCREF _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA #define DK_DEBUG_DECREF _Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA #define DK_INCREF(dk) (DK_DEBUG_INCREF ++(dk)->dk_refcnt) #define DK_DECREF(dk) if (DK_DEBUG_DECREF (--(dk)->dk_refcnt) == 0) free_keys_object(dk) #define DK_MASK(dk) (((dk)->dk_size)-1) #define IS_POWER_OF_2(x) (((x) & (x-1)) == 0) /* lookup indices. returns DKIX_EMPTY, DKIX_DUMMY, or ix >=0 */ static inline Py_ssize_t dk_get_index(PyDictKeysObject *keys, Py_ssize_t i) { Py_ssize_t s = DK_SIZE(keys); Py_ssize_t ix; if (s <= 0xff) { int8_t *indices = (int8_t*)(keys->dk_indices); ix = indices[i]; } else if (s <= 0xffff) { int16_t *indices = (int16_t*)(keys->dk_indices); ix = indices[i]; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { int64_t *indices = (int64_t*)(keys->dk_indices); ix = indices[i]; } #endif else { int32_t *indices = (int32_t*)(keys->dk_indices); ix = indices[i]; } assert(ix >= DKIX_DUMMY); return ix; } /* write to indices. */ static inline void dk_set_index(PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix) { Py_ssize_t s = DK_SIZE(keys); assert(ix >= DKIX_DUMMY); if (s <= 0xff) { int8_t *indices = (int8_t*)(keys->dk_indices); assert(ix <= 0x7f); indices[i] = (char)ix; } else if (s <= 0xffff) { int16_t *indices = (int16_t*)(keys->dk_indices); assert(ix <= 0x7fff); indices[i] = (int16_t)ix; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { int64_t *indices = (int64_t*)(keys->dk_indices); indices[i] = ix; } #endif else { int32_t *indices = (int32_t*)(keys->dk_indices); assert(ix <= 0x7fffffff); indices[i] = (int32_t)ix; } } /* USABLE_FRACTION is the maximum dictionary load. * Increasing this ratio makes dictionaries more dense resulting in more * collisions. Decreasing it improves sparseness at the expense of spreading * indices over more cache lines and at the cost of total memory consumed. * * USABLE_FRACTION must obey the following: * (0 < USABLE_FRACTION(n) < n) for all n >= 2 * * USABLE_FRACTION should be quick to calculate. * Fractions around 1/2 to 2/3 seem to work well in practice. */ #define USABLE_FRACTION(n) (((n) << 1)/3) /* ESTIMATE_SIZE is reverse function of USABLE_FRACTION. * This can be used to reserve enough size to insert n entries without * resizing. */ #define ESTIMATE_SIZE(n) (((n)*3+1) >> 1) /* Alternative fraction that is otherwise close enough to 2n/3 to make * little difference. 8 * 2/3 == 8 * 5/8 == 5. 16 * 2/3 == 16 * 5/8 == 10. * 32 * 2/3 = 21, 32 * 5/8 = 20. * Its advantage is that it is faster to compute on machines with slow division. * #define USABLE_FRACTION(n) (((n) >> 1) + ((n) >> 2) - ((n) >> 3)) */ /* GROWTH_RATE. Growth rate upon hitting maximum load. * Currently set to used*3. * This means that dicts double in size when growing without deletions, * but have more head room when the number of deletions is on a par with the * number of insertions. See also bpo-17563 and bpo-33205. * * GROWTH_RATE was set to used*4 up to version 3.2. * GROWTH_RATE was set to used*2 in version 3.3.0 * GROWTH_RATE was set to used*2 + capacity/2 in 3.4.0-3.6.0. */ #define GROWTH_RATE(d) ((d)->ma_used*3) #define ENSURE_ALLOWS_DELETIONS(d) \ if ((d)->ma_keys->dk_lookup == lookdict_unicode_nodummy) { \ (d)->ma_keys->dk_lookup = lookdict_unicode; \ } /* This immutable, empty PyDictKeysObject is used for PyDict_Clear() * (which cannot fail and thus can do no allocation). */ static PyDictKeysObject empty_keys_struct = { 1, /* dk_refcnt */ 1, /* dk_size */ lookdict_split, /* dk_lookup */ 0, /* dk_usable (immutable) */ 0, /* dk_nentries */ {DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY}, /* dk_indices */ }; static PyObject *empty_values[1] = { NULL }; #define Py_EMPTY_KEYS &empty_keys_struct /* Uncomment to check the dict content in _PyDict_CheckConsistency() */ /* #define DEBUG_PYDICT */ #ifndef NDEBUG static int _PyDict_CheckConsistency(PyDictObject *mp) { PyDictKeysObject *keys = mp->ma_keys; int splitted = _PyDict_HasSplitTable(mp); Py_ssize_t usable = USABLE_FRACTION(keys->dk_size); #ifdef DEBUG_PYDICT PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i; #endif assert(0 <= mp->ma_used && mp->ma_used <= usable); assert(IS_POWER_OF_2(keys->dk_size)); assert(0 <= keys->dk_usable && keys->dk_usable <= usable); assert(0 <= keys->dk_nentries && keys->dk_nentries <= usable); assert(keys->dk_usable + keys->dk_nentries <= usable); if (!splitted) { /* combined table */ assert(keys->dk_refcnt == 1); } #ifdef DEBUG_PYDICT for (i=0; i < keys->dk_size; i++) { Py_ssize_t ix = dk_get_index(keys, i); assert(DKIX_DUMMY <= ix && ix <= usable); } for (i=0; i < usable; i++) { PyDictKeyEntry *entry = &entries[i]; PyObject *key = entry->me_key; if (key != NULL) { if (PyUnicode_CheckExact(key)) { Py_hash_t hash = ((PyASCIIObject *)key)->hash; assert(hash != -1); assert(entry->me_hash == hash); } else { /* test_dict fails if PyObject_Hash() is called again */ assert(entry->me_hash != -1); } if (!splitted) { assert(entry->me_value != NULL); } } if (splitted) { assert(entry->me_value == NULL); } } if (splitted) { /* splitted table */ for (i=0; i < mp->ma_used; i++) { assert(mp->ma_values[i] != NULL); } } #endif return 1; } #endif static PyDictKeysObject *new_keys_object(Py_ssize_t size) { PyDictKeysObject *dk; Py_ssize_t es, usable; assert(size >= PyDict_MINSIZE); assert(IS_POWER_OF_2(size)); usable = USABLE_FRACTION(size); if (size <= 0xff) { es = 1; } else if (size <= 0xffff) { es = 2; } #if SIZEOF_VOID_P > 4 else if (size <= 0xffffffff) { es = 4; } #endif else { es = sizeof(Py_ssize_t); } if (size == PyDict_MINSIZE && numfreekeys > 0) { dk = keys_free_list[--numfreekeys]; } else { dk = PyObject_MALLOC(sizeof(PyDictKeysObject) + es * size + sizeof(PyDictKeyEntry) * usable); if (dk == NULL) { PyErr_NoMemory(); return NULL; } } DK_DEBUG_INCREF dk->dk_refcnt = 1; dk->dk_size = size; dk->dk_usable = usable; dk->dk_lookup = lookdict_unicode_nodummy; dk->dk_nentries = 0; memset(&dk->dk_indices[0], 0xff, es * size); memset(DK_ENTRIES(dk), 0, sizeof(PyDictKeyEntry) * usable); return dk; } static void free_keys_object(PyDictKeysObject *keys) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n; for (i = 0, n = keys->dk_nentries; i < n; i++) { Py_XDECREF(entries[i].me_key); Py_XDECREF(entries[i].me_value); } if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) { keys_free_list[numfreekeys++] = keys; return; } PyObject_FREE(keys); } #define new_values(size) PyMem_NEW(PyObject *, size) #define free_values(values) PyMem_FREE(values) /* Consumes a reference to the keys object */ static PyObject * new_dict(PyDictKeysObject *keys, PyObject **values) { PyDictObject *mp; assert(keys != NULL); if (numfree) { mp = free_list[--numfree]; assert (mp != NULL); assert (Py_TYPE(mp) == &PyDict_Type); _Py_NewReference((PyObject *)mp); } else { mp = PyObject_GC_New(PyDictObject, &PyDict_Type); if (mp == NULL) { DK_DECREF(keys); free_values(values); return NULL; } } mp->ma_keys = keys; mp->ma_values = values; mp->ma_used = 0; mp->ma_version_tag = DICT_NEXT_VERSION(); assert(_PyDict_CheckConsistency(mp)); return (PyObject *)mp; } /* Consumes a reference to the keys object */ static PyObject * new_dict_with_shared_keys(PyDictKeysObject *keys) { PyObject **values; Py_ssize_t i, size; size = USABLE_FRACTION(DK_SIZE(keys)); values = new_values(size); if (values == NULL) { DK_DECREF(keys); return PyErr_NoMemory(); } for (i = 0; i < size; i++) { values[i] = NULL; } return new_dict(keys, values); } static PyObject * clone_combined_dict(PyDictObject *orig) { assert(PyDict_CheckExact(orig)); assert(orig->ma_values == NULL); assert(orig->ma_keys->dk_refcnt == 1); Py_ssize_t keys_size = _PyDict_KeysSize(orig->ma_keys); PyDictKeysObject *keys = PyObject_Malloc(keys_size); if (keys == NULL) { PyErr_NoMemory(); return NULL; } memcpy(keys, orig->ma_keys, keys_size); /* After copying key/value pairs, we need to incref all keys and values and they are about to be co-owned by a new dict object. */ PyDictKeyEntry *ep0 = DK_ENTRIES(keys); Py_ssize_t n = keys->dk_nentries; for (Py_ssize_t i = 0; i < n; i++) { PyDictKeyEntry *entry = &ep0[i]; PyObject *value = entry->me_value; if (value != NULL) { Py_INCREF(value); Py_INCREF(entry->me_key); } } PyDictObject *new = (PyDictObject *)new_dict(keys, NULL); if (new == NULL) { /* In case of an error, `new_dict()` takes care of cleaning up `keys`. */ return NULL; } new->ma_used = orig->ma_used; assert(_PyDict_CheckConsistency(new)); if (_PyObject_GC_IS_TRACKED(orig)) { /* Maintain tracking. */ _PyObject_GC_TRACK(new); } /* Since we copied the keys table we now have an extra reference in the system. Manually call _Py_INC_REFTOTAL to signal that we have it now; calling DK_INCREF would be an error as keys->dk_refcnt is already set to 1 (after memcpy). */ _Py_INC_REFTOTAL; return (PyObject *)new; } PyObject * PyDict_New(void) { PyDictKeysObject *keys = new_keys_object(PyDict_MINSIZE); if (keys == NULL) return NULL; return new_dict(keys, NULL); } /* Search index of hash table from offset of entry table */ static Py_ssize_t lookdict_index(PyDictKeysObject *k, Py_hash_t hash, Py_ssize_t index) { size_t mask = DK_MASK(k); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dk_get_index(k, i); if (ix == index) { return i; } if (ix == DKIX_EMPTY) { return DKIX_EMPTY; } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } /* The basic lookup function used by all operations. This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4. Open addressing is preferred over chaining since the link overhead for chaining would be substantial (100% with typical malloc overhead). The initial probe index is computed as hash mod the table size. Subsequent probe indices are computed as explained earlier. All arithmetic on hash should ignore overflow. The details in this version are due to Tim Peters, building on many past contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and Christian Tismer. lookdict() is general-purpose, and may return DKIX_ERROR if (and only if) a comparison raises an exception. lookdict_unicode() below is specialized to string keys, comparison of which can never raise an exception; that function can never return DKIX_ERROR when key is string. Otherwise, it falls back to lookdict(). lookdict_unicode_nodummy is further specialized for string keys that cannot be the value. For both, when the key isn't found a DKIX_EMPTY is returned. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { size_t i, mask, perturb; PyDictKeysObject *dk; PyDictKeyEntry *ep0; top: dk = mp->ma_keys; ep0 = DK_ENTRIES(dk); mask = DK_MASK(dk); perturb = hash; i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dk_get_index(dk, i); if (ix == DKIX_EMPTY) { *value_addr = NULL; return ix; } if (ix >= 0) { PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); if (ep->me_key == key) { *value_addr = ep->me_value; return ix; } if (ep->me_hash == hash) { PyObject *startkey = ep->me_key; Py_INCREF(startkey); int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); Py_DECREF(startkey); if (cmp < 0) { *value_addr = NULL; return DKIX_ERROR; } if (dk == mp->ma_keys && ep->me_key == startkey) { if (cmp > 0) { *value_addr = ep->me_value; return ix; } } else { /* The dict was mutated, restart */ goto top; } } } perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; } Py_UNREACHABLE(); } /* Specialized version for string-only keys */ static Py_ssize_t _Py_HOT_FUNCTION lookdict_unicode(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { assert(mp->ma_values == NULL); /* Make sure this function doesn't have to handle non-unicode keys, including subclasses of str; e.g., one reason to subclass unicodes is to override __eq__, and for speed we don't cater to that here. */ if (!PyUnicode_CheckExact(key)) { mp->ma_keys->dk_lookup = lookdict; return lookdict(mp, key, hash, value_addr); } PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); size_t mask = DK_MASK(mp->ma_keys); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dk_get_index(mp->ma_keys, i); if (ix == DKIX_EMPTY) { *value_addr = NULL; return DKIX_EMPTY; } if (ix >= 0) { PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { *value_addr = ep->me_value; return ix; } } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } /* Faster version of lookdict_unicode when it is known that no keys * will be present. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { assert(mp->ma_values == NULL); /* Make sure this function doesn't have to handle non-unicode keys, including subclasses of str; e.g., one reason to subclass unicodes is to override __eq__, and for speed we don't cater to that here. */ if (!PyUnicode_CheckExact(key)) { mp->ma_keys->dk_lookup = lookdict; return lookdict(mp, key, hash, value_addr); } PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); size_t mask = DK_MASK(mp->ma_keys); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dk_get_index(mp->ma_keys, i); assert (ix != DKIX_DUMMY); if (ix == DKIX_EMPTY) { *value_addr = NULL; return DKIX_EMPTY; } PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { *value_addr = ep->me_value; return ix; } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } /* Version of lookdict for split tables. * All split tables and only split tables use this lookup function. * Split tables only contain unicode keys and no dummy keys, * so algorithm is the same as lookdict_unicode_nodummy. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict_split(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { /* mp must split table */ assert(mp->ma_values != NULL); if (!PyUnicode_CheckExact(key)) { Py_ssize_t ix = lookdict(mp, key, hash, value_addr); if (ix >= 0) { *value_addr = mp->ma_values[ix]; } return ix; } PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); size_t mask = DK_MASK(mp->ma_keys); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dk_get_index(mp->ma_keys, i); assert (ix != DKIX_DUMMY); if (ix == DKIX_EMPTY) { *value_addr = NULL; return DKIX_EMPTY; } PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { *value_addr = mp->ma_values[ix]; return ix; } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } int _PyDict_HasOnlyStringKeys(PyObject *dict) { Py_ssize_t pos = 0; PyObject *key, *value; assert(PyDict_Check(dict)); /* Shortcut */ if (((PyDictObject *)dict)->ma_keys->dk_lookup != lookdict) return 1; while (PyDict_Next(dict, &pos, &key, &value)) if (!PyUnicode_Check(key)) return 0; return 1; } #define MAINTAIN_TRACKING(mp, key, value) \ do { \ if (!_PyObject_GC_IS_TRACKED(mp)) { \ if (_PyObject_GC_MAY_BE_TRACKED(key) || \ _PyObject_GC_MAY_BE_TRACKED(value)) { \ _PyObject_GC_TRACK(mp); \ } \ } \ } while(0) void _PyDict_MaybeUntrack(PyObject *op) { PyDictObject *mp; PyObject *value; Py_ssize_t i, numentries; PyDictKeyEntry *ep0; if (!PyDict_CheckExact(op) || !_PyObject_GC_IS_TRACKED(op)) return; mp = (PyDictObject *) op; ep0 = DK_ENTRIES(mp->ma_keys); numentries = mp->ma_keys->dk_nentries; if (_PyDict_HasSplitTable(mp)) { for (i = 0; i < numentries; i++) { if ((value = mp->ma_values[i]) == NULL) continue; if (_PyObject_GC_MAY_BE_TRACKED(value)) { assert(!_PyObject_GC_MAY_BE_TRACKED(ep0[i].me_key)); return; } } } else { for (i = 0; i < numentries; i++) { if ((value = ep0[i].me_value) == NULL) continue; if (_PyObject_GC_MAY_BE_TRACKED(value) || _PyObject_GC_MAY_BE_TRACKED(ep0[i].me_key)) return; } } _PyObject_GC_UNTRACK(op); } /* Internal function to find slot for an item from its hash when it is known that the key is not present in the dict. The dict must be combined. */ static Py_ssize_t find_empty_slot(PyDictKeysObject *keys, Py_hash_t hash) { assert(keys != NULL); const size_t mask = DK_MASK(keys); size_t i = hash & mask; Py_ssize_t ix = dk_get_index(keys, i); for (size_t perturb = hash; ix >= 0;) { perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; ix = dk_get_index(keys, i); } return i; } static int insertion_resize(PyDictObject *mp) { return dictresize(mp, GROWTH_RATE(mp)); } /* Internal routine to insert a new item into the table. Used both by the internal resize routine and by the public insert routine. Returns -1 if an error occurred, or 0 on success. */ static int insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) { PyObject *old_value; PyDictKeyEntry *ep; Py_INCREF(key); Py_INCREF(value); if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) { if (insertion_resize(mp) < 0) goto Fail; } Py_ssize_t ix = mp->ma_keys->dk_lookup(mp, key, hash, &old_value); if (ix == DKIX_ERROR) goto Fail; assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict); MAINTAIN_TRACKING(mp, key, value); /* When insertion order is different from shared key, we can't share * the key anymore. Convert this instance to combine table. */ if (_PyDict_HasSplitTable(mp) && ((ix >= 0 && old_value == NULL && mp->ma_used != ix) || (ix == DKIX_EMPTY && mp->ma_used != mp->ma_keys->dk_nentries))) { if (insertion_resize(mp) < 0) goto Fail; ix = DKIX_EMPTY; } if (ix == DKIX_EMPTY) { /* Insert into new slot. */ assert(old_value == NULL); if (mp->ma_keys->dk_usable <= 0) { /* Need to resize. */ if (insertion_resize(mp) < 0) goto Fail; } Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash); ep = &DK_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries]; dk_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries); ep->me_key = key; ep->me_hash = hash; if (mp->ma_values) { assert (mp->ma_values[mp->ma_keys->dk_nentries] == NULL); mp->ma_values[mp->ma_keys->dk_nentries] = value; } else { ep->me_value = value; } mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); mp->ma_keys->dk_usable--; mp->ma_keys->dk_nentries++; assert(mp->ma_keys->dk_usable >= 0); assert(_PyDict_CheckConsistency(mp)); return 0; } if (_PyDict_HasSplitTable(mp)) { mp->ma_values[ix] = value; if (old_value == NULL) { /* pending state */ assert(ix == mp->ma_used); mp->ma_used++; } } else { assert(old_value != NULL); DK_ENTRIES(mp->ma_keys)[ix].me_value = value; } mp->ma_version_tag = DICT_NEXT_VERSION(); Py_XDECREF(old_value); /* which **CAN** re-enter (see issue #22653) */ assert(_PyDict_CheckConsistency(mp)); Py_DECREF(key); return 0; Fail: Py_DECREF(value); Py_DECREF(key); return -1; } /* Internal routine used by dictresize() to build a hashtable of entries. */ static void build_indices(PyDictKeysObject *keys, PyDictKeyEntry *ep, Py_ssize_t n) { size_t mask = (size_t)DK_SIZE(keys) - 1; for (Py_ssize_t ix = 0; ix != n; ix++, ep++) { Py_hash_t hash = ep->me_hash; size_t i = hash & mask; for (size_t perturb = hash; dk_get_index(keys, i) != DKIX_EMPTY;) { perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } dk_set_index(keys, i, ix); } } /* Restructure the table by allocating a new table and reinserting all items again. When entries have been deleted, the new table may actually be smaller than the old one. If a table is split (its keys and hashes are shared, its values are not), then the values are temporarily copied into the table, it is resized as a combined table, then the me_value slots in the old table are NULLed out. After resizing a table is always combined, but can be resplit by make_keys_shared(). */ static int dictresize(PyDictObject *mp, Py_ssize_t minsize) { Py_ssize_t newsize, numentries; PyDictKeysObject *oldkeys; PyObject **oldvalues; PyDictKeyEntry *oldentries, *newentries; /* Find the smallest table size > minused. */ for (newsize = PyDict_MINSIZE; newsize < minsize && newsize > 0; newsize <<= 1) ; if (newsize <= 0) { PyErr_NoMemory(); return -1; } oldkeys = mp->ma_keys; /* NOTE: Current odict checks mp->ma_keys to detect resize happen. * So we can't reuse oldkeys even if oldkeys->dk_size == newsize. * TODO: Try reusing oldkeys when reimplement odict. */ /* Allocate a new table. */ mp->ma_keys = new_keys_object(newsize); if (mp->ma_keys == NULL) { mp->ma_keys = oldkeys; return -1; } // New table must be large enough. assert(mp->ma_keys->dk_usable >= mp->ma_used); if (oldkeys->dk_lookup == lookdict) mp->ma_keys->dk_lookup = lookdict; numentries = mp->ma_used; oldentries = DK_ENTRIES(oldkeys); newentries = DK_ENTRIES(mp->ma_keys); oldvalues = mp->ma_values; if (oldvalues != NULL) { /* Convert split table into new combined table. * We must incref keys; we can transfer values. * Note that values of split table is always dense. */ for (Py_ssize_t i = 0; i < numentries; i++) { assert(oldvalues[i] != NULL); PyDictKeyEntry *ep = &oldentries[i]; PyObject *key = ep->me_key; Py_INCREF(key); newentries[i].me_key = key; newentries[i].me_hash = ep->me_hash; newentries[i].me_value = oldvalues[i]; } DK_DECREF(oldkeys); mp->ma_values = NULL; if (oldvalues != empty_values) { free_values(oldvalues); } } else { // combined table. if (oldkeys->dk_nentries == numentries) { memcpy(newentries, oldentries, numentries * sizeof(PyDictKeyEntry)); } else { PyDictKeyEntry *ep = oldentries; for (Py_ssize_t i = 0; i < numentries; i++) { while (ep->me_value == NULL) ep++; newentries[i] = *ep++; } } assert(oldkeys->dk_lookup != lookdict_split); assert(oldkeys->dk_refcnt == 1); if (oldkeys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) { DK_DEBUG_DECREF keys_free_list[numfreekeys++] = oldkeys; } else { DK_DEBUG_DECREF PyObject_FREE(oldkeys); } } build_indices(mp->ma_keys, newentries, numentries); mp->ma_keys->dk_usable -= numentries; mp->ma_keys->dk_nentries = numentries; return 0; } /* Returns NULL if unable to split table. * A NULL return does not necessarily indicate an error */ static PyDictKeysObject * make_keys_shared(PyObject *op) { Py_ssize_t i; Py_ssize_t size; PyDictObject *mp = (PyDictObject *)op; if (!PyDict_CheckExact(op)) return NULL; if (!_PyDict_HasSplitTable(mp)) { PyDictKeyEntry *ep0; PyObject **values; assert(mp->ma_keys->dk_refcnt == 1); if (mp->ma_keys->dk_lookup == lookdict) { return NULL; } else if (mp->ma_keys->dk_lookup == lookdict_unicode) { /* Remove dummy keys */ if (dictresize(mp, DK_SIZE(mp->ma_keys))) return NULL; } assert(mp->ma_keys->dk_lookup == lookdict_unicode_nodummy); /* Copy values into a new array */ ep0 = DK_ENTRIES(mp->ma_keys); size = USABLE_FRACTION(DK_SIZE(mp->ma_keys)); values = new_values(size); if (values == NULL) { PyErr_SetString(PyExc_MemoryError, "Not enough memory to allocate new values array"); return NULL; } for (i = 0; i < size; i++) { values[i] = ep0[i].me_value; ep0[i].me_value = NULL; } mp->ma_keys->dk_lookup = lookdict_split; mp->ma_values = values; } DK_INCREF(mp->ma_keys); return mp->ma_keys; } PyObject * _PyDict_NewPresized(Py_ssize_t minused) { const Py_ssize_t max_presize = 128 * 1024; Py_ssize_t newsize; PyDictKeysObject *new_keys; /* There are no strict guarantee that returned dict can contain minused * items without resize. So we create medium size dict instead of very * large dict or MemoryError. */ if (minused > USABLE_FRACTION(max_presize)) { newsize = max_presize; } else { Py_ssize_t minsize = ESTIMATE_SIZE(minused); newsize = PyDict_MINSIZE; while (newsize < minsize) { newsize <<= 1; } } assert(IS_POWER_OF_2(newsize)); new_keys = new_keys_object(newsize); if (new_keys == NULL) return NULL; return new_dict(new_keys, NULL); } /* Note that, for historical reasons, PyDict_GetItem() suppresses all errors * that may occur (originally dicts supported only string keys, and exceptions * weren't possible). So, while the original intent was that a NULL return * meant the key wasn't present, in reality it can mean that, or that an error * (suppressed) occurred while computing the key's hash, or that some error * (suppressed) occurred when comparing keys in the dict's internal probe * sequence. A nasty example of the latter is when a Python-coded comparison * function hits a stack-depth error, which can cause this to return NULL * even if the key is present. */ PyObject * PyDict_GetItem(PyObject *op, PyObject *key) { Py_hash_t hash; Py_ssize_t ix; PyDictObject *mp = (PyDictObject *)op; PyThreadState *tstate; PyObject *value; if (!PyDict_Check(op)) return NULL; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { PyErr_Clear(); return NULL; } } /* We can arrive here with a NULL tstate during initialization: try running "python -Wi" for an example related to string interning. Let's just hope that no exception occurs then... This must be _PyThreadState_Current and not PyThreadState_GET() because in debug mode, the latter complains if tstate is NULL. */ tstate = PyThreadState_GET(); if (tstate != NULL && tstate->curexc_type != NULL) { /* preserve the existing exception */ PyObject *err_type, *err_value, *err_tb; PyErr_Fetch(&err_type, &err_value, &err_tb); ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); /* ignore errors */ PyErr_Restore(err_type, err_value, err_tb); if (ix < 0) return NULL; } else { ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix < 0) { PyErr_Clear(); return NULL; } } return value; } /* Same as PyDict_GetItemWithError() but with hash supplied by caller. This returns NULL *with* an exception set if an exception occurred. It returns NULL *without* an exception set if the key wasn't present. */ PyObject * _PyDict_GetItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) { Py_ssize_t ix; PyDictObject *mp = (PyDictObject *)op; PyObject *value; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix < 0) { return NULL; } return value; } /* Variant of PyDict_GetItem() that doesn't suppress exceptions. This returns NULL *with* an exception set if an exception occurred. It returns NULL *without* an exception set if the key wasn't present. */ PyObject * PyDict_GetItemWithError(PyObject *op, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyDictObject*mp = (PyDictObject *)op; PyObject *value; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { return NULL; } } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix < 0) return NULL; return value; } PyObject * _PyDict_GetItemIdWithError(PyObject *dp, struct _Py_Identifier *key) { PyObject *kv; kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) return NULL; return PyDict_GetItemWithError(dp, kv); } /* Fast version of global value lookup (LOAD_GLOBAL). * Lookup in globals, then builtins. * * Raise an exception and return NULL if an error occurred (ex: computing the * key hash failed, key comparison failed, ...). Return NULL if the key doesn't * exist. Return the value if the key exists. */ PyObject * _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } /* namespace 1: globals */ ix = globals->ma_keys->dk_lookup(globals, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix != DKIX_EMPTY && value != NULL) return value; /* namespace 2: builtins */ ix = builtins->ma_keys->dk_lookup(builtins, key, hash, &value); if (ix < 0) return NULL; return value; } /* CAUTION: PyDict_SetItem() must guarantee that it won't resize the * dictionary if it's merely replacing the value for an existing key. * This means that it's safe to loop over a dictionary with PyDict_Next() * and occasionally replace a value -- but you can't insert new keys or * remove them. */ int PyDict_SetItem(PyObject *op, PyObject *key, PyObject *value) { PyDictObject *mp; Py_hash_t hash; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); assert(value); mp = (PyDictObject *)op; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; } /* insertdict() handles any resizing that might be necessary */ return insertdict(mp, key, hash, value); } int _PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value, Py_hash_t hash) { PyDictObject *mp; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); assert(value); assert(hash != -1); mp = (PyDictObject *)op; /* insertdict() handles any resizing that might be necessary */ return insertdict(mp, key, hash, value); } static int delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix, PyObject *old_value) { PyObject *old_key; PyDictKeyEntry *ep; Py_ssize_t hashpos = lookdict_index(mp->ma_keys, hash, ix); assert(hashpos >= 0); mp->ma_used--; mp->ma_version_tag = DICT_NEXT_VERSION(); ep = &DK_ENTRIES(mp->ma_keys)[ix]; dk_set_index(mp->ma_keys, hashpos, DKIX_DUMMY); ENSURE_ALLOWS_DELETIONS(mp); old_key = ep->me_key; ep->me_key = NULL; ep->me_value = NULL; Py_DECREF(old_key); Py_DECREF(old_value); assert(_PyDict_CheckConsistency(mp)); return 0; } int PyDict_DelItem(PyObject *op, PyObject *key) { Py_hash_t hash; assert(key); if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; } return _PyDict_DelItem_KnownHash(op, key, hash); } int _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) { Py_ssize_t ix; PyDictObject *mp; PyObject *old_value; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); assert(hash != -1); mp = (PyDictObject *)op; ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); if (ix == DKIX_ERROR) return -1; if (ix == DKIX_EMPTY || old_value == NULL) { _PyErr_SetKeyError(key); return -1; } // Split table doesn't allow deletion. Combine it. if (_PyDict_HasSplitTable(mp)) { if (dictresize(mp, DK_SIZE(mp->ma_keys))) { return -1; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); assert(ix >= 0); } return delitem_common(mp, hash, ix, old_value); } /* This function promises that the predicate -> deletion sequence is atomic * (i.e. protected by the GIL), assuming the predicate itself doesn't * release the GIL. */ int _PyDict_DelItemIf(PyObject *op, PyObject *key, int (*predicate)(PyObject *value)) { Py_ssize_t hashpos, ix; PyDictObject *mp; Py_hash_t hash; PyObject *old_value; int res; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); hash = PyObject_Hash(key); if (hash == -1) return -1; mp = (PyDictObject *)op; ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); if (ix == DKIX_ERROR) return -1; if (ix == DKIX_EMPTY || old_value == NULL) { _PyErr_SetKeyError(key); return -1; } // Split table doesn't allow deletion. Combine it. if (_PyDict_HasSplitTable(mp)) { if (dictresize(mp, DK_SIZE(mp->ma_keys))) { return -1; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); assert(ix >= 0); } res = predicate(old_value); if (res == -1) return -1; hashpos = lookdict_index(mp->ma_keys, hash, ix); assert(hashpos >= 0); if (res > 0) return delitem_common(mp, hashpos, ix, old_value); else return 0; } void PyDict_Clear(PyObject *op) { PyDictObject *mp; PyDictKeysObject *oldkeys; PyObject **oldvalues; Py_ssize_t i, n; if (!PyDict_Check(op)) return; mp = ((PyDictObject *)op); oldkeys = mp->ma_keys; oldvalues = mp->ma_values; if (oldvalues == empty_values) return; /* Empty the dict... */ DK_INCREF(Py_EMPTY_KEYS); mp->ma_keys = Py_EMPTY_KEYS; mp->ma_values = empty_values; mp->ma_used = 0; mp->ma_version_tag = DICT_NEXT_VERSION(); /* ...then clear the keys and values */ if (oldvalues != NULL) { n = oldkeys->dk_nentries; for (i = 0; i < n; i++) Py_CLEAR(oldvalues[i]); free_values(oldvalues); DK_DECREF(oldkeys); } else { assert(oldkeys->dk_refcnt == 1); DK_DECREF(oldkeys); } assert(_PyDict_CheckConsistency(mp)); } /* Internal version of PyDict_Next that returns a hash value in addition * to the key and value. * Return 1 on success, return 0 when the reached the end of the dictionary * (or if op is not a dictionary) */ int _PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue, Py_hash_t *phash) { Py_ssize_t i; PyDictObject *mp; PyDictKeyEntry *entry_ptr; PyObject *value; if (!PyDict_Check(op)) return 0; mp = (PyDictObject *)op; i = *ppos; if (mp->ma_values) { if (i < 0 || i >= mp->ma_used) return 0; /* values of split table is always dense */ entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; value = mp->ma_values[i]; assert(value != NULL); } else { Py_ssize_t n = mp->ma_keys->dk_nentries; if (i < 0 || i >= n) return 0; entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) return 0; value = entry_ptr->me_value; } *ppos = i+1; if (pkey) *pkey = entry_ptr->me_key; if (phash) *phash = entry_ptr->me_hash; if (pvalue) *pvalue = value; return 1; } /* * Iterate over a dict. Use like so: * * Py_ssize_t i; * PyObject *key, *value; * i = 0; # important! i should not otherwise be changed by you * while (PyDict_Next(yourdict, &i, &key, &value)) { * Refer to borrowed references in key and value. * } * * Return 1 on success, return 0 when the reached the end of the dictionary * (or if op is not a dictionary) * * CAUTION: In general, it isn't safe to use PyDict_Next in a loop that * mutates the dict. One exception: it is safe if the loop merely changes * the values associated with the keys (but doesn't insert new keys or * delete keys), via PyDict_SetItem(). */ int PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) { return _PyDict_Next(op, ppos, pkey, pvalue, NULL); } /* Internal version of dict.pop(). */ PyObject * _PyDict_Pop_KnownHash(PyObject *dict, PyObject *key, Py_hash_t hash, PyObject *deflt) { Py_ssize_t ix, hashpos; PyObject *old_value, *old_key; PyDictKeyEntry *ep; PyDictObject *mp; assert(PyDict_Check(dict)); mp = (PyDictObject *)dict; if (mp->ma_used == 0) { if (deflt) { Py_INCREF(deflt); return deflt; } _PyErr_SetKeyError(key); return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || old_value == NULL) { if (deflt) { Py_INCREF(deflt); return deflt; } _PyErr_SetKeyError(key); return NULL; } // Split table doesn't allow deletion. Combine it. if (_PyDict_HasSplitTable(mp)) { if (dictresize(mp, DK_SIZE(mp->ma_keys))) { return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); assert(ix >= 0); } hashpos = lookdict_index(mp->ma_keys, hash, ix); assert(hashpos >= 0); assert(old_value != NULL); mp->ma_used--; mp->ma_version_tag = DICT_NEXT_VERSION(); dk_set_index(mp->ma_keys, hashpos, DKIX_DUMMY); ep = &DK_ENTRIES(mp->ma_keys)[ix]; ENSURE_ALLOWS_DELETIONS(mp); old_key = ep->me_key; ep->me_key = NULL; ep->me_value = NULL; Py_DECREF(old_key); assert(_PyDict_CheckConsistency(mp)); return old_value; } PyObject * _PyDict_Pop(PyObject *dict, PyObject *key, PyObject *deflt) { Py_hash_t hash; if (((PyDictObject *)dict)->ma_used == 0) { if (deflt) { Py_INCREF(deflt); return deflt; } _PyErr_SetKeyError(key); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } return _PyDict_Pop_KnownHash(dict, key, hash, deflt); } /* Internal version of dict.from_keys(). It is subclass-friendly. */ PyObject * _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) { PyObject *it; /* iter(iterable) */ PyObject *key; PyObject *d; int status; d = _PyObject_CallNoArg(cls); if (d == NULL) return NULL; if (PyDict_CheckExact(d) && ((PyDictObject *)d)->ma_used == 0) { if (PyDict_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; PyObject *oldvalue; Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; if (dictresize(mp, ESTIMATE_SIZE(PyDict_GET_SIZE(iterable)))) { Py_DECREF(d); return NULL; } while (_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) { if (insertdict(mp, key, hash, value)) { Py_DECREF(d); return NULL; } } return d; } if (PyAnySet_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; if (dictresize(mp, ESTIMATE_SIZE(PySet_GET_SIZE(iterable)))) { Py_DECREF(d); return NULL; } while (_PySet_NextEntry(iterable, &pos, &key, &hash)) { if (insertdict(mp, key, hash, value)) { Py_DECREF(d); return NULL; } } return d; } } it = PyObject_GetIter(iterable); if (it == NULL){ Py_DECREF(d); return NULL; } if (PyDict_CheckExact(d)) { while ((key = PyIter_Next(it)) != NULL) { status = PyDict_SetItem(d, key, value); Py_DECREF(key); if (status < 0) goto Fail; } } else { while ((key = PyIter_Next(it)) != NULL) { status = PyObject_SetItem(d, key, value); Py_DECREF(key); if (status < 0) goto Fail; } } if (PyErr_Occurred()) goto Fail; Py_DECREF(it); return d; Fail: Py_DECREF(it); Py_DECREF(d); return NULL; } /* Methods */ static void dict_dealloc(PyDictObject *mp) { PyObject **values = mp->ma_values; PyDictKeysObject *keys = mp->ma_keys; Py_ssize_t i, n; /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(mp); Py_TRASHCAN_SAFE_BEGIN(mp) if (values != NULL) { if (values != empty_values) { for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) { Py_XDECREF(values[i]); } free_values(values); } DK_DECREF(keys); } else if (keys != NULL) { assert(keys->dk_refcnt == 1); DK_DECREF(keys); } if (numfree < PyDict_MAXFREELIST && Py_TYPE(mp) == &PyDict_Type) free_list[numfree++] = mp; else Py_TYPE(mp)->tp_free((PyObject *)mp); Py_TRASHCAN_SAFE_END(mp) } static PyObject * dict_repr(PyDictObject *mp) { Py_ssize_t i; PyObject *key = NULL, *value = NULL; _PyUnicodeWriter writer; int first; i = Py_ReprEnter((PyObject *)mp); if (i != 0) { return i > 0 ? PyUnicode_FromString("{...}") : NULL; } if (mp->ma_used == 0) { Py_ReprLeave((PyObject *)mp); return PyUnicode_FromString("{}"); } _PyUnicodeWriter_Init(&writer); writer.overallocate = 1; /* "{" + "1: 2" + ", 3: 4" * (len - 1) + "}" */ writer.min_length = 1 + 4 + (2 + 4) * (mp->ma_used - 1) + 1; if (_PyUnicodeWriter_WriteChar(&writer, '{') < 0) goto error; /* Do repr() on each key+value pair, and insert ": " between them. Note that repr may mutate the dict. */ i = 0; first = 1; while (PyDict_Next((PyObject *)mp, &i, &key, &value)) { PyObject *s; int res; /* Prevent repr from deleting key or value during key format. */ Py_INCREF(key); Py_INCREF(value); if (!first) { if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0) goto error; } first = 0; s = PyObject_Repr(key); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; if (_PyUnicodeWriter_WriteASCIIString(&writer, ": ", 2) < 0) goto error; s = PyObject_Repr(value); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; Py_CLEAR(key); Py_CLEAR(value); } writer.overallocate = 0; if (_PyUnicodeWriter_WriteChar(&writer, '}') < 0) goto error; Py_ReprLeave((PyObject *)mp); return _PyUnicodeWriter_Finish(&writer); error: Py_ReprLeave((PyObject *)mp); _PyUnicodeWriter_Dealloc(&writer); Py_XDECREF(key); Py_XDECREF(value); return NULL; } static Py_ssize_t dict_length(PyDictObject *mp) { return mp->ma_used; } static PyObject * dict_subscript(PyDictObject *mp, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || value == NULL) { if (!PyDict_CheckExact(mp)) { /* Look up __missing__ method if we're a subclass. */ PyObject *missing, *res; _Py_IDENTIFIER(__missing__); missing = _PyObject_LookupSpecial((PyObject *)mp, &PyId___missing__); if (missing != NULL) { res = PyObject_CallFunctionObjArgs(missing, key, NULL); Py_DECREF(missing); return res; } else if (PyErr_Occurred()) return NULL; } _PyErr_SetKeyError(key); return NULL; } Py_INCREF(value); return value; } static int dict_ass_sub(PyDictObject *mp, PyObject *v, PyObject *w) { if (w == NULL) return PyDict_DelItem((PyObject *)mp, v); else return PyDict_SetItem((PyObject *)mp, v, w); } static PyMappingMethods dict_as_mapping = { (lenfunc)dict_length, /*mp_length*/ (binaryfunc)dict_subscript, /*mp_subscript*/ (objobjargproc)dict_ass_sub, /*mp_ass_subscript*/ }; static PyObject * dict_keys(PyDictObject *mp) { PyObject *v; Py_ssize_t i, j; PyDictKeyEntry *ep; Py_ssize_t size, n, offset; PyObject **value_ptr; again: n = mp->ma_used; v = PyList_New(n); if (v == NULL) return NULL; if (n != mp->ma_used) { /* Durnit. The allocations caused the dict to resize. * Just start over, this shouldn't normally happen. */ Py_DECREF(v); goto again; } ep = DK_ENTRIES(mp->ma_keys); size = mp->ma_keys->dk_nentries; if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *); } else { value_ptr = &ep[0].me_value; offset = sizeof(PyDictKeyEntry); } for (i = 0, j = 0; i < size; i++) { if (*value_ptr != NULL) { PyObject *key = ep[i].me_key; Py_INCREF(key); PyList_SET_ITEM(v, j, key); j++; } value_ptr = (PyObject **)(((char *)value_ptr) + offset); } assert(j == n); return v; } static PyObject * dict_values(PyDictObject *mp) { PyObject *v; Py_ssize_t i, j; PyDictKeyEntry *ep; Py_ssize_t size, n, offset; PyObject **value_ptr; again: n = mp->ma_used; v = PyList_New(n); if (v == NULL) return NULL; if (n != mp->ma_used) { /* Durnit. The allocations caused the dict to resize. * Just start over, this shouldn't normally happen. */ Py_DECREF(v); goto again; } ep = DK_ENTRIES(mp->ma_keys); size = mp->ma_keys->dk_nentries; if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *); } else { value_ptr = &ep[0].me_value; offset = sizeof(PyDictKeyEntry); } for (i = 0, j = 0; i < size; i++) { PyObject *value = *value_ptr; value_ptr = (PyObject **)(((char *)value_ptr) + offset); if (value != NULL) { Py_INCREF(value); PyList_SET_ITEM(v, j, value); j++; } } assert(j == n); return v; } static PyObject * dict_items(PyDictObject *mp) { PyObject *v; Py_ssize_t i, j, n; Py_ssize_t size, offset; PyObject *item, *key; PyDictKeyEntry *ep; PyObject **value_ptr; /* Preallocate the list of tuples, to avoid allocations during * the loop over the items, which could trigger GC, which * could resize the dict. :-( */ again: n = mp->ma_used; v = PyList_New(n); if (v == NULL) return NULL; for (i = 0; i < n; i++) { item = PyTuple_New(2); if (item == NULL) { Py_DECREF(v); return NULL; } PyList_SET_ITEM(v, i, item); } if (n != mp->ma_used) { /* Durnit. The allocations caused the dict to resize. * Just start over, this shouldn't normally happen. */ Py_DECREF(v); goto again; } /* Nothing we do below makes any function calls. */ ep = DK_ENTRIES(mp->ma_keys); size = mp->ma_keys->dk_nentries; if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *); } else { value_ptr = &ep[0].me_value; offset = sizeof(PyDictKeyEntry); } for (i = 0, j = 0; i < size; i++) { PyObject *value = *value_ptr; value_ptr = (PyObject **)(((char *)value_ptr) + offset); if (value != NULL) { key = ep[i].me_key; item = PyList_GET_ITEM(v, j); Py_INCREF(key); PyTuple_SET_ITEM(item, 0, key); Py_INCREF(value); PyTuple_SET_ITEM(item, 1, value); j++; } } assert(j == n); return v; } /*[clinic input] @classmethod dict.fromkeys iterable: object value: object=None / Create a new dictionary with keys from iterable and values set to value. [clinic start generated code]*/ static PyObject * dict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value) /*[clinic end generated code: output=8fb98e4b10384999 input=382ba4855d0f74c3]*/ { return _PyDict_FromKeys((PyObject *)type, iterable, value); } static int dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, const char *methname) { PyObject *arg = NULL; int result = 0; if (!PyArg_UnpackTuple(args, methname, 0, 1, &arg)) { result = -1; } else if (arg != NULL) { _Py_IDENTIFIER(keys); PyObject *func; if (_PyObject_LookupAttrId(arg, &PyId_keys, &func) < 0) { result = -1; } else if (func != NULL) { Py_DECREF(func); result = PyDict_Merge(self, arg, 1); } else { result = PyDict_MergeFromSeq2(self, arg, 1); } } if (result == 0 && kwds != NULL) { if (PyArg_ValidateKeywordArguments(kwds)) result = PyDict_Merge(self, kwds, 1); else result = -1; } return result; } /* Note: dict.update() uses the METH_VARARGS|METH_KEYWORDS calling convention. Using METH_FASTCALL|METH_KEYWORDS would make dict.update(**dict2) calls slower, see the issue #29312. */ static PyObject * dict_update(PyObject *self, PyObject *args, PyObject *kwds) { if (dict_update_common(self, args, kwds, "update") != -1) Py_RETURN_NONE; return NULL; } /* Update unconditionally replaces existing items. Merge has a 3rd argument 'override'; if set, it acts like Update, otherwise it leaves existing items unchanged. PyDict_{Update,Merge} update/merge from a mapping object. PyDict_MergeFromSeq2 updates/merges from any iterable object producing iterable objects of length 2. */ int PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override) { PyObject *it; /* iter(seq2) */ Py_ssize_t i; /* index into seq2 of current element */ PyObject *item; /* seq2[i] */ PyObject *fast; /* item as a 2-tuple or 2-list */ assert(d != NULL); assert(PyDict_Check(d)); assert(seq2 != NULL); it = PyObject_GetIter(seq2); if (it == NULL) return -1; for (i = 0; ; ++i) { PyObject *key, *value; Py_ssize_t n; fast = NULL; item = PyIter_Next(it); if (item == NULL) { if (PyErr_Occurred()) goto Fail; break; } /* Convert item to sequence, and verify length 2. */ fast = PySequence_Fast(item, ""); if (fast == NULL) { if (PyErr_ExceptionMatches(PyExc_TypeError)) PyErr_Format(PyExc_TypeError, "cannot convert dictionary update " "sequence element #%zd to a sequence", i); goto Fail; } n = PySequence_Fast_GET_SIZE(fast); if (n != 2) { PyErr_Format(PyExc_ValueError, "dictionary update sequence element #%zd " "has length %zd; 2 is required", i, n); goto Fail; } /* Update/merge with this (key, value) pair. */ key = PySequence_Fast_GET_ITEM(fast, 0); value = PySequence_Fast_GET_ITEM(fast, 1); Py_INCREF(key); Py_INCREF(value); if (override || PyDict_GetItem(d, key) == NULL) { int status = PyDict_SetItem(d, key, value); if (status < 0) { Py_DECREF(key); Py_DECREF(value); goto Fail; } } Py_DECREF(key); Py_DECREF(value); Py_DECREF(fast); Py_DECREF(item); } i = 0; assert(_PyDict_CheckConsistency((PyDictObject *)d)); goto Return; Fail: Py_XDECREF(item); Py_XDECREF(fast); i = -1; Return: Py_DECREF(it); return Py_SAFE_DOWNCAST(i, Py_ssize_t, int); } static int dict_merge(PyObject *a, PyObject *b, int override) { PyDictObject *mp, *other; Py_ssize_t i, n; PyDictKeyEntry *entry, *ep0; assert(0 <= override && override <= 2); /* We accept for the argument either a concrete dictionary object, * or an abstract "mapping" object. For the former, we can do * things quite efficiently. For the latter, we only require that * PyMapping_Keys() and PyObject_GetItem() be supported. */ if (a == NULL || !PyDict_Check(a) || b == NULL) { PyErr_BadInternalCall(); return -1; } mp = (PyDictObject*)a; if (PyDict_Check(b) && (Py_TYPE(b)->tp_iter == (getiterfunc)dict_iter)) { other = (PyDictObject*)b; if (other == mp || other->ma_used == 0) /* a.update(a) or a.update({}); nothing to do */ return 0; if (mp->ma_used == 0) /* Since the target dict is empty, PyDict_GetItem() * always returns NULL. Setting override to 1 * skips the unnecessary test. */ override = 1; /* Do one big resize at the start, rather than * incrementally resizing as we insert new items. Expect * that there will be no (or few) overlapping keys. */ if (USABLE_FRACTION(mp->ma_keys->dk_size) < other->ma_used) { if (dictresize(mp, ESTIMATE_SIZE(mp->ma_used + other->ma_used))) { return -1; } } ep0 = DK_ENTRIES(other->ma_keys); for (i = 0, n = other->ma_keys->dk_nentries; i < n; i++) { PyObject *key, *value; Py_hash_t hash; entry = &ep0[i]; key = entry->me_key; hash = entry->me_hash; if (other->ma_values) value = other->ma_values[i]; else value = entry->me_value; if (value != NULL) { int err = 0; Py_INCREF(key); Py_INCREF(value); if (override == 1) err = insertdict(mp, key, hash, value); else if (_PyDict_GetItem_KnownHash(a, key, hash) == NULL) { if (PyErr_Occurred()) { Py_DECREF(value); Py_DECREF(key); return -1; } err = insertdict(mp, key, hash, value); } else if (override != 0) { _PyErr_SetKeyError(key); Py_DECREF(value); Py_DECREF(key); return -1; } Py_DECREF(value); Py_DECREF(key); if (err != 0) return -1; if (n != other->ma_keys->dk_nentries) { PyErr_SetString(PyExc_RuntimeError, "dict mutated during update"); return -1; } } } } else { /* Do it the generic, slower way */ PyObject *keys = PyMapping_Keys(b); PyObject *iter; PyObject *key, *value; int status; if (keys == NULL) /* Docstring says this is equivalent to E.keys() so * if E doesn't have a .keys() method we want * AttributeError to percolate up. Might as well * do the same for any other error. */ return -1; iter = PyObject_GetIter(keys); Py_DECREF(keys); if (iter == NULL) return -1; for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) { if (override != 1 && PyDict_GetItem(a, key) != NULL) { if (override != 0) { _PyErr_SetKeyError(key); Py_DECREF(key); Py_DECREF(iter); return -1; } Py_DECREF(key); continue; } value = PyObject_GetItem(b, key); if (value == NULL) { Py_DECREF(iter); Py_DECREF(key); return -1; } status = PyDict_SetItem(a, key, value); Py_DECREF(key); Py_DECREF(value); if (status < 0) { Py_DECREF(iter); return -1; } } Py_DECREF(iter); if (PyErr_Occurred()) /* Iterator completed, via error */ return -1; } assert(_PyDict_CheckConsistency((PyDictObject *)a)); return 0; } int PyDict_Update(PyObject *a, PyObject *b) { return dict_merge(a, b, 1); } int PyDict_Merge(PyObject *a, PyObject *b, int override) { /* XXX Deprecate override not in (0, 1). */ return dict_merge(a, b, override != 0); } int _PyDict_MergeEx(PyObject *a, PyObject *b, int override) { return dict_merge(a, b, override); } static PyObject * dict_copy(PyDictObject *mp) { return PyDict_Copy((PyObject*)mp); } PyObject * PyDict_Copy(PyObject *o) { PyObject *copy; PyDictObject *mp; Py_ssize_t i, n; if (o == NULL || !PyDict_Check(o)) { PyErr_BadInternalCall(); return NULL; } mp = (PyDictObject *)o; if (mp->ma_used == 0) { /* The dict is empty; just return a new dict. */ return PyDict_New(); } if (_PyDict_HasSplitTable(mp)) { PyDictObject *split_copy; Py_ssize_t size = USABLE_FRACTION(DK_SIZE(mp->ma_keys)); PyObject **newvalues; newvalues = new_values(size); if (newvalues == NULL) return PyErr_NoMemory(); split_copy = PyObject_GC_New(PyDictObject, &PyDict_Type); if (split_copy == NULL) { free_values(newvalues); return NULL; } split_copy->ma_values = newvalues; split_copy->ma_keys = mp->ma_keys; split_copy->ma_used = mp->ma_used; split_copy->ma_version_tag = DICT_NEXT_VERSION(); DK_INCREF(mp->ma_keys); for (i = 0, n = size; i < n; i++) { PyObject *value = mp->ma_values[i]; Py_XINCREF(value); split_copy->ma_values[i] = value; } if (_PyObject_GC_IS_TRACKED(mp)) _PyObject_GC_TRACK(split_copy); return (PyObject *)split_copy; } if (PyDict_CheckExact(mp) && mp->ma_values == NULL && (mp->ma_used >= (mp->ma_keys->dk_nentries * 2) / 3)) { /* Use fast-copy if: (1) 'mp' is an instance of a subclassed dict; and (2) 'mp' is not a split-dict; and (3) if 'mp' is non-compact ('del' operation does not resize dicts), do fast-copy only if it has at most 1/3 non-used keys. The last condition (3) is important to guard against a pathological case when a large dict is almost emptied with multiple del/pop operations and copied after that. In cases like this, we defer to PyDict_Merge, which produces a compacted copy. */ return clone_combined_dict(mp); } copy = PyDict_New(); if (copy == NULL) return NULL; if (PyDict_Merge(copy, o, 1) == 0) return copy; Py_DECREF(copy); return NULL; } Py_ssize_t PyDict_Size(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return -1; } return ((PyDictObject *)mp)->ma_used; } PyObject * PyDict_Keys(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return NULL; } return dict_keys((PyDictObject *)mp); } PyObject * PyDict_Values(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return NULL; } return dict_values((PyDictObject *)mp); } PyObject * PyDict_Items(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return NULL; } return dict_items((PyDictObject *)mp); } /* Return 1 if dicts equal, 0 if not, -1 if error. * Gets out as soon as any difference is detected. * Uses only Py_EQ comparison. */ static int dict_equal(PyDictObject *a, PyDictObject *b) { Py_ssize_t i; if (a->ma_used != b->ma_used) /* can't be equal if # of entries differ */ return 0; /* Same # of entries -- check all of 'em. Exit early on any diff. */ for (i = 0; i < a->ma_keys->dk_nentries; i++) { PyDictKeyEntry *ep = &DK_ENTRIES(a->ma_keys)[i]; PyObject *aval; if (a->ma_values) aval = a->ma_values[i]; else aval = ep->me_value; if (aval != NULL) { int cmp; PyObject *bval; PyObject *key = ep->me_key; /* temporarily bump aval's refcount to ensure it stays alive until we're done with it */ Py_INCREF(aval); /* ditto for key */ Py_INCREF(key); /* reuse the known hash value */ b->ma_keys->dk_lookup(b, key, ep->me_hash, &bval); if (bval == NULL) { Py_DECREF(key); Py_DECREF(aval); if (PyErr_Occurred()) return -1; return 0; } Py_INCREF(bval); cmp = PyObject_RichCompareBool(aval, bval, Py_EQ); Py_DECREF(key); Py_DECREF(aval); Py_DECREF(bval); if (cmp <= 0) /* error or not equal */ return cmp; } } return 1; } static PyObject * dict_richcompare(PyObject *v, PyObject *w, int op) { int cmp; PyObject *res; if (!PyDict_Check(v) || !PyDict_Check(w)) { res = Py_NotImplemented; } else if (op == Py_EQ || op == Py_NE) { cmp = dict_equal((PyDictObject *)v, (PyDictObject *)w); if (cmp < 0) return NULL; res = (cmp == (op == Py_EQ)) ? Py_True : Py_False; } else res = Py_NotImplemented; Py_INCREF(res); return res; } /*[clinic input] @coexist dict.__contains__ key: object / True if the dictionary has the specified key, else False. [clinic start generated code]*/ static PyObject * dict___contains__(PyDictObject *self, PyObject *key) /*[clinic end generated code: output=a3d03db709ed6e6b input=fe1cb42ad831e820]*/ { register PyDictObject *mp = self; Py_hash_t hash; Py_ssize_t ix; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || value == NULL) Py_RETURN_FALSE; Py_RETURN_TRUE; } /*[clinic input] dict.get key: object default: object = None / Return the value for key if key is in the dictionary, else default. [clinic start generated code]*/ static PyObject * dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value) /*[clinic end generated code: output=bba707729dee05bf input=279ddb5790b6b107]*/ { PyObject *val = NULL; Py_hash_t hash; Py_ssize_t ix; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (self->ma_keys->dk_lookup) (self, key, hash, &val); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || val == NULL) { val = default_value; } Py_INCREF(val); return val; } PyObject * PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj) { PyDictObject *mp = (PyDictObject *)d; PyObject *value; Py_hash_t hash; if (!PyDict_Check(d)) { PyErr_BadInternalCall(); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) { if (insertion_resize(mp) < 0) return NULL; } Py_ssize_t ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (_PyDict_HasSplitTable(mp) && ((ix >= 0 && value == NULL && mp->ma_used != ix) || (ix == DKIX_EMPTY && mp->ma_used != mp->ma_keys->dk_nentries))) { if (insertion_resize(mp) < 0) { return NULL; } ix = DKIX_EMPTY; } if (ix == DKIX_EMPTY) { PyDictKeyEntry *ep, *ep0; value = defaultobj; if (mp->ma_keys->dk_usable <= 0) { if (insertion_resize(mp) < 0) { return NULL; } } Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash); ep0 = DK_ENTRIES(mp->ma_keys); ep = &ep0[mp->ma_keys->dk_nentries]; dk_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries); Py_INCREF(key); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); ep->me_key = key; ep->me_hash = hash; if (_PyDict_HasSplitTable(mp)) { assert(mp->ma_values[mp->ma_keys->dk_nentries] == NULL); mp->ma_values[mp->ma_keys->dk_nentries] = value; } else { ep->me_value = value; } mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); mp->ma_keys->dk_usable--; mp->ma_keys->dk_nentries++; assert(mp->ma_keys->dk_usable >= 0); } else if (value == NULL) { value = defaultobj; assert(_PyDict_HasSplitTable(mp)); assert(ix == mp->ma_used); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); mp->ma_values[ix] = value; mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); } assert(_PyDict_CheckConsistency(mp)); return value; } /*[clinic input] dict.setdefault key: object default: object = None / Insert key with a value of default if key is not in the dictionary. Return the value for key if key is in the dictionary, else default. [clinic start generated code]*/ static PyObject * dict_setdefault_impl(PyDictObject *self, PyObject *key, PyObject *default_value) /*[clinic end generated code: output=f8c1101ebf69e220 input=0f063756e815fd9d]*/ { PyObject *val; val = PyDict_SetDefault((PyObject *)self, key, default_value); Py_XINCREF(val); return val; } static PyObject * dict_clear(PyDictObject *mp) { PyDict_Clear((PyObject *)mp); Py_RETURN_NONE; } static PyObject * dict_pop(PyDictObject *mp, PyObject *args) { PyObject *key, *deflt = NULL; if(!PyArg_UnpackTuple(args, "pop", 1, 2, &key, &deflt)) return NULL; return _PyDict_Pop((PyObject*)mp, key, deflt); } static PyObject * dict_popitem(PyDictObject *mp) { Py_ssize_t i, j; PyDictKeyEntry *ep0, *ep; PyObject *res; /* Allocate the result tuple before checking the size. Believe it * or not, this allocation could trigger a garbage collection which * could empty the dict, so if we checked the size first and that * happened, the result would be an infinite loop (searching for an * entry that no longer exists). Note that the usual popitem() * idiom is "while d: k, v = d.popitem()". so needing to throw the * tuple away if the dict *is* empty isn't a significant * inefficiency -- possible, but unlikely in practice. */ res = PyTuple_New(2); if (res == NULL) return NULL; if (mp->ma_used == 0) { Py_DECREF(res); PyErr_SetString(PyExc_KeyError, "popitem(): dictionary is empty"); return NULL; } /* Convert split table to combined table */ if (mp->ma_keys->dk_lookup == lookdict_split) { if (dictresize(mp, DK_SIZE(mp->ma_keys))) { Py_DECREF(res); return NULL; } } ENSURE_ALLOWS_DELETIONS(mp); /* Pop last item */ ep0 = DK_ENTRIES(mp->ma_keys); i = mp->ma_keys->dk_nentries - 1; while (i >= 0 && ep0[i].me_value == NULL) { i--; } assert(i >= 0); ep = &ep0[i]; j = lookdict_index(mp->ma_keys, ep->me_hash, i); assert(j >= 0); assert(dk_get_index(mp->ma_keys, j) == i); dk_set_index(mp->ma_keys, j, DKIX_DUMMY); PyTuple_SET_ITEM(res, 0, ep->me_key); PyTuple_SET_ITEM(res, 1, ep->me_value); ep->me_key = NULL; ep->me_value = NULL; /* We can't dk_usable++ since there is DKIX_DUMMY in indices */ mp->ma_keys->dk_nentries = i; mp->ma_used--; mp->ma_version_tag = DICT_NEXT_VERSION(); assert(_PyDict_CheckConsistency(mp)); return res; } static int dict_traverse(PyObject *op, visitproc visit, void *arg) { PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n = keys->dk_nentries; if (keys->dk_lookup == lookdict) { for (i = 0; i < n; i++) { if (entries[i].me_value != NULL) { Py_VISIT(entries[i].me_value); Py_VISIT(entries[i].me_key); } } } else { if (mp->ma_values != NULL) { for (i = 0; i < n; i++) { Py_VISIT(mp->ma_values[i]); } } else { for (i = 0; i < n; i++) { Py_VISIT(entries[i].me_value); } } } return 0; } static int dict_tp_clear(PyObject *op) { PyDict_Clear(op); return 0; } static PyObject *dictiter_new(PyDictObject *, PyTypeObject *); Py_ssize_t _PyDict_SizeOf(PyDictObject *mp) { Py_ssize_t size, usable, res; size = DK_SIZE(mp->ma_keys); usable = USABLE_FRACTION(size); res = _PyObject_SIZE(Py_TYPE(mp)); if (mp->ma_values) res += usable * sizeof(PyObject*); /* If the dictionary is split, the keys portion is accounted-for in the type object. */ if (mp->ma_keys->dk_refcnt == 1) res += (sizeof(PyDictKeysObject) + DK_IXSIZE(mp->ma_keys) * size + sizeof(PyDictKeyEntry) * usable); return res; } Py_ssize_t _PyDict_KeysSize(PyDictKeysObject *keys) { return (sizeof(PyDictKeysObject) + DK_IXSIZE(keys) * DK_SIZE(keys) + USABLE_FRACTION(DK_SIZE(keys)) * sizeof(PyDictKeyEntry)); } static PyObject * dict_sizeof(PyDictObject *mp) { return PyLong_FromSsize_t(_PyDict_SizeOf(mp)); } PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]"); PyDoc_STRVAR(sizeof__doc__, "D.__sizeof__() -> size of D in memory, in bytes"); PyDoc_STRVAR(pop__doc__, "D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\n\ If key is not found, d is returned if given, otherwise KeyError is raised"); PyDoc_STRVAR(popitem__doc__, "D.popitem() -> (k, v), remove and return some (key, value) pair as a\n\ 2-tuple; but raise KeyError if D is empty."); PyDoc_STRVAR(update__doc__, "D.update([E, ]**F) -> None. Update D from dict/iterable E and F.\n\ If E is present and has a .keys() method, then does: for k in E: D[k] = E[k]\n\ If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v\n\ In either case, this is followed by: for k in F: D[k] = F[k]"); PyDoc_STRVAR(clear__doc__, "D.clear() -> None. Remove all items from D."); PyDoc_STRVAR(copy__doc__, "D.copy() -> a shallow copy of D"); /* Forward */ static PyObject *dictkeys_new(PyObject *); static PyObject *dictitems_new(PyObject *); static PyObject *dictvalues_new(PyObject *); PyDoc_STRVAR(keys__doc__, "D.keys() -> a set-like object providing a view on D's keys"); PyDoc_STRVAR(items__doc__, "D.items() -> a set-like object providing a view on D's items"); PyDoc_STRVAR(values__doc__, "D.values() -> an object providing a view on D's values"); static PyMethodDef mapp_methods[] = { DICT___CONTAINS___METHODDEF {"__getitem__", (PyCFunction)dict_subscript, METH_O | METH_COEXIST, getitem__doc__}, {"__sizeof__", (PyCFunction)dict_sizeof, METH_NOARGS, sizeof__doc__}, DICT_GET_METHODDEF DICT_SETDEFAULT_METHODDEF {"pop", (PyCFunction)dict_pop, METH_VARARGS, pop__doc__}, {"popitem", (PyCFunction)dict_popitem, METH_NOARGS, popitem__doc__}, {"keys", (PyCFunction)dictkeys_new, METH_NOARGS, keys__doc__}, {"items", (PyCFunction)dictitems_new, METH_NOARGS, items__doc__}, {"values", (PyCFunction)dictvalues_new, METH_NOARGS, values__doc__}, {"update", (PyCFunction)dict_update, METH_VARARGS | METH_KEYWORDS, update__doc__}, DICT_FROMKEYS_METHODDEF {"clear", (PyCFunction)dict_clear, METH_NOARGS, clear__doc__}, {"copy", (PyCFunction)dict_copy, METH_NOARGS, copy__doc__}, {NULL, NULL} /* sentinel */ }; /* Return 1 if `key` is in dict `op`, 0 if not, and -1 on error. */ int PyDict_Contains(PyObject *op, PyObject *key) { Py_hash_t hash; Py_ssize_t ix; PyDictObject *mp = (PyDictObject *)op; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return -1; return (ix != DKIX_EMPTY && value != NULL); } /* Internal version of PyDict_Contains used when the hash value is already known */ int _PyDict_Contains(PyObject *op, PyObject *key, Py_hash_t hash) { PyDictObject *mp = (PyDictObject *)op; PyObject *value; Py_ssize_t ix; ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return -1; return (ix != DKIX_EMPTY && value != NULL); } /* Hack to implement "key in dict" */ static PySequenceMethods dict_as_sequence = { 0, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ PyDict_Contains, /* sq_contains */ 0, /* sq_inplace_concat */ 0, /* sq_inplace_repeat */ }; static PyObject * dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *self; PyDictObject *d; assert(type != NULL && type->tp_alloc != NULL); self = type->tp_alloc(type, 0); if (self == NULL) return NULL; d = (PyDictObject *)self; /* The object has been implicitly tracked by tp_alloc */ if (type == &PyDict_Type) _PyObject_GC_UNTRACK(d); d->ma_used = 0; d->ma_version_tag = DICT_NEXT_VERSION(); d->ma_keys = new_keys_object(PyDict_MINSIZE); if (d->ma_keys == NULL) { Py_DECREF(self); return NULL; } assert(_PyDict_CheckConsistency(d)); return self; } static int dict_init(PyObject *self, PyObject *args, PyObject *kwds) { return dict_update_common(self, args, kwds, "dict"); } static PyObject * dict_iter(PyDictObject *dict) { return dictiter_new(dict, &PyDictIterKey_Type); } PyDoc_STRVAR(dictionary_doc, "dict() -> new empty dictionary\n" "dict(mapping) -> new dictionary initialized from a mapping object's\n" " (key, value) pairs\n" "dict(iterable) -> new dictionary initialized as if via:\n" " d = {}\n" " for k, v in iterable:\n" " d[k] = v\n" "dict(**kwargs) -> new dictionary initialized with the name=value pairs\n" " in the keyword argument list. For example: dict(one=1, two=2)"); PyTypeObject PyDict_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict", sizeof(PyDictObject), 0, (destructor)dict_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ (reprfunc)dict_repr, /* tp_repr */ 0, /* tp_as_number */ &dict_as_sequence, /* tp_as_sequence */ &dict_as_mapping, /* tp_as_mapping */ PyObject_HashNotImplemented, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_DICT_SUBCLASS, /* tp_flags */ dictionary_doc, /* tp_doc */ dict_traverse, /* tp_traverse */ dict_tp_clear, /* tp_clear */ dict_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dict_iter, /* tp_iter */ 0, /* tp_iternext */ mapp_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ dict_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ dict_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ }; PyObject * _PyDict_GetItemId(PyObject *dp, struct _Py_Identifier *key) { PyObject *kv; kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) { PyErr_Clear(); return NULL; } return PyDict_GetItem(dp, kv); } /* For backward compatibility with old dictionary interface */ PyObject * PyDict_GetItemString(PyObject *v, const char *key) { PyObject *kv, *rv; kv = PyUnicode_FromString(key); if (kv == NULL) { PyErr_Clear(); return NULL; } rv = PyDict_GetItem(v, kv); Py_DECREF(kv); return rv; } int _PyDict_SetItemId(PyObject *v, struct _Py_Identifier *key, PyObject *item) { PyObject *kv; kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) return -1; return PyDict_SetItem(v, kv, item); } int PyDict_SetItemString(PyObject *v, const char *key, PyObject *item) { PyObject *kv; int err; kv = PyUnicode_FromString(key); if (kv == NULL) return -1; PyUnicode_InternInPlace(&kv); /* XXX Should we really? */ err = PyDict_SetItem(v, kv, item); Py_DECREF(kv); return err; } int _PyDict_DelItemId(PyObject *v, _Py_Identifier *key) { PyObject *kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) return -1; return PyDict_DelItem(v, kv); } int PyDict_DelItemString(PyObject *v, const char *key) { PyObject *kv; int err; kv = PyUnicode_FromString(key); if (kv == NULL) return -1; err = PyDict_DelItem(v, kv); Py_DECREF(kv); return err; } /* Dictionary iterator types */ typedef struct { PyObject_HEAD PyDictObject *di_dict; /* Set to NULL when iterator is exhausted */ Py_ssize_t di_used; Py_ssize_t di_pos; PyObject* di_result; /* reusable result tuple for iteritems */ Py_ssize_t len; } dictiterobject; static PyObject * dictiter_new(PyDictObject *dict, PyTypeObject *itertype) { dictiterobject *di; di = PyObject_GC_New(dictiterobject, itertype); if (di == NULL) return NULL; Py_INCREF(dict); di->di_dict = dict; di->di_used = dict->ma_used; di->di_pos = 0; di->len = dict->ma_used; if (itertype == &PyDictIterItem_Type) { di->di_result = PyTuple_Pack(2, Py_None, Py_None); if (di->di_result == NULL) { Py_DECREF(di); return NULL; } } else di->di_result = NULL; _PyObject_GC_TRACK(di); return (PyObject *)di; } static void dictiter_dealloc(dictiterobject *di) { /* bpo-31095: UnTrack is needed before calling any callbacks */ _PyObject_GC_UNTRACK(di); Py_XDECREF(di->di_dict); Py_XDECREF(di->di_result); PyObject_GC_Del(di); } static int dictiter_traverse(dictiterobject *di, visitproc visit, void *arg) { Py_VISIT(di->di_dict); Py_VISIT(di->di_result); return 0; } static PyObject * dictiter_len(dictiterobject *di) { Py_ssize_t len = 0; if (di->di_dict != NULL && di->di_used == di->di_dict->ma_used) len = di->len; return PyLong_FromSize_t(len); } PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); static PyObject * dictiter_reduce(dictiterobject *di); PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyMethodDef dictiter_methods[] = { {"__length_hint__", (PyCFunction)dictiter_len, METH_NOARGS, length_hint_doc}, {"__reduce__", (PyCFunction)dictiter_reduce, METH_NOARGS, reduce_doc}, {NULL, NULL} /* sentinel */ }; static PyObject* dictiter_iternextkey(dictiterobject *di) { PyObject *key; Py_ssize_t i; PyDictKeysObject *k; PyDictObject *d = di->di_dict; if (d == NULL) return NULL; assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } i = di->di_pos; k = d->ma_keys; assert(i >= 0); if (d->ma_values) { if (i >= d->ma_used) goto fail; key = DK_ENTRIES(k)[i].me_key; assert(d->ma_values[i] != NULL); } else { Py_ssize_t n = k->dk_nentries; PyDictKeyEntry *entry_ptr = &DK_ENTRIES(k)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) goto fail; key = entry_ptr->me_key; } di->di_pos = i+1; di->len--; Py_INCREF(key); return key; fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictIterKey_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_keyiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)dictiter_iternextkey, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject * dictiter_iternextvalue(dictiterobject *di) { PyObject *value; Py_ssize_t i; PyDictObject *d = di->di_dict; if (d == NULL) return NULL; assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } i = di->di_pos; assert(i >= 0); if (d->ma_values) { if (i >= d->ma_used) goto fail; value = d->ma_values[i]; assert(value != NULL); } else { Py_ssize_t n = d->ma_keys->dk_nentries; PyDictKeyEntry *entry_ptr = &DK_ENTRIES(d->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) goto fail; value = entry_ptr->me_value; } di->di_pos = i+1; di->len--; Py_INCREF(value); return value; fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictIterValue_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_valueiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)dictiter_iternextvalue, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject * dictiter_iternextitem(dictiterobject *di) { PyObject *key, *value, *result; Py_ssize_t i; PyDictObject *d = di->di_dict; if (d == NULL) return NULL; assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } i = di->di_pos; assert(i >= 0); if (d->ma_values) { if (i >= d->ma_used) goto fail; key = DK_ENTRIES(d->ma_keys)[i].me_key; value = d->ma_values[i]; assert(value != NULL); } else { Py_ssize_t n = d->ma_keys->dk_nentries; PyDictKeyEntry *entry_ptr = &DK_ENTRIES(d->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) goto fail; key = entry_ptr->me_key; value = entry_ptr->me_value; } di->di_pos = i+1; di->len--; Py_INCREF(key); Py_INCREF(value); result = di->di_result; if (Py_REFCNT(result) == 1) { PyObject *oldkey = PyTuple_GET_ITEM(result, 0); PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); } else { result = PyTuple_New(2); if (result == NULL) return NULL; PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ } return result; fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictIterItem_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_itemiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)dictiter_iternextitem, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject * dictiter_reduce(dictiterobject *di) { PyObject *list; dictiterobject tmp; list = PyList_New(0); if (!list) return NULL; /* copy the itertor state */ tmp = *di; Py_XINCREF(tmp.di_dict); /* iterate the temporary into a list */ for(;;) { PyObject *element = 0; if (Py_TYPE(di) == &PyDictIterItem_Type) element = dictiter_iternextitem(&tmp); else if (Py_TYPE(di) == &PyDictIterKey_Type) element = dictiter_iternextkey(&tmp); else if (Py_TYPE(di) == &PyDictIterValue_Type) element = dictiter_iternextvalue(&tmp); else Py_UNREACHABLE(); if (element) { if (PyList_Append(list, element)) { Py_DECREF(element); Py_DECREF(list); Py_XDECREF(tmp.di_dict); return NULL; } Py_DECREF(element); } else break; } Py_XDECREF(tmp.di_dict); /* check for error */ if (tmp.di_dict != NULL) { /* we have an error */ Py_DECREF(list); return NULL; } return Py_BuildValue("N(N)", _PyObject_GetBuiltin("iter"), list); } /***********************************************/ /* View objects for keys(), items(), values(). */ /***********************************************/ /* The instance lay-out is the same for all three; but the type differs. */ static void dictview_dealloc(_PyDictViewObject *dv) { /* bpo-31095: UnTrack is needed before calling any callbacks */ _PyObject_GC_UNTRACK(dv); Py_XDECREF(dv->dv_dict); PyObject_GC_Del(dv); } static int dictview_traverse(_PyDictViewObject *dv, visitproc visit, void *arg) { Py_VISIT(dv->dv_dict); return 0; } static Py_ssize_t dictview_len(_PyDictViewObject *dv) { Py_ssize_t len = 0; if (dv->dv_dict != NULL) len = dv->dv_dict->ma_used; return len; } PyObject * _PyDictView_New(PyObject *dict, PyTypeObject *type) { _PyDictViewObject *dv; if (dict == NULL) { PyErr_BadInternalCall(); return NULL; } if (!PyDict_Check(dict)) { /* XXX Get rid of this restriction later */ PyErr_Format(PyExc_TypeError, "%s() requires a dict argument, not '%s'", type->tp_name, dict->ob_type->tp_name); return NULL; } dv = PyObject_GC_New(_PyDictViewObject, type); if (dv == NULL) return NULL; Py_INCREF(dict); dv->dv_dict = (PyDictObject *)dict; _PyObject_GC_TRACK(dv); return (PyObject *)dv; } /* TODO(guido): The views objects are not complete: * support more set operations * support arbitrary mappings? - either these should be static or exported in dictobject.h - if public then they should probably be in builtins */ /* Return 1 if self is a subset of other, iterating over self; 0 if not; -1 if an error occurred. */ static int all_contained_in(PyObject *self, PyObject *other) { PyObject *iter = PyObject_GetIter(self); int ok = 1; if (iter == NULL) return -1; for (;;) { PyObject *next = PyIter_Next(iter); if (next == NULL) { if (PyErr_Occurred()) ok = -1; break; } ok = PySequence_Contains(other, next); Py_DECREF(next); if (ok <= 0) break; } Py_DECREF(iter); return ok; } static PyObject * dictview_richcompare(PyObject *self, PyObject *other, int op) { Py_ssize_t len_self, len_other; int ok; PyObject *result; assert(self != NULL); assert(PyDictViewSet_Check(self)); assert(other != NULL); if (!PyAnySet_Check(other) && !PyDictViewSet_Check(other)) Py_RETURN_NOTIMPLEMENTED; len_self = PyObject_Size(self); if (len_self < 0) return NULL; len_other = PyObject_Size(other); if (len_other < 0) return NULL; ok = 0; switch(op) { case Py_NE: case Py_EQ: if (len_self == len_other) ok = all_contained_in(self, other); if (op == Py_NE && ok >= 0) ok = !ok; break; case Py_LT: if (len_self < len_other) ok = all_contained_in(self, other); break; case Py_LE: if (len_self <= len_other) ok = all_contained_in(self, other); break; case Py_GT: if (len_self > len_other) ok = all_contained_in(other, self); break; case Py_GE: if (len_self >= len_other) ok = all_contained_in(other, self); break; } if (ok < 0) return NULL; result = ok ? Py_True : Py_False; Py_INCREF(result); return result; } static PyObject * dictview_repr(_PyDictViewObject *dv) { PyObject *seq; PyObject *result = NULL; Py_ssize_t rc; rc = Py_ReprEnter((PyObject *)dv); if (rc != 0) { return rc > 0 ? PyUnicode_FromString("...") : NULL; } seq = PySequence_List((PyObject *)dv); if (seq == NULL) { goto Done; } result = PyUnicode_FromFormat("%s(%R)", Py_TYPE(dv)->tp_name, seq); Py_DECREF(seq); Done: Py_ReprLeave((PyObject *)dv); return result; } /*** dict_keys ***/ static PyObject * dictkeys_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictIterKey_Type); } static int dictkeys_contains(_PyDictViewObject *dv, PyObject *obj) { if (dv->dv_dict == NULL) return 0; return PyDict_Contains((PyObject *)dv->dv_dict, obj); } static PySequenceMethods dictkeys_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictkeys_contains, /* sq_contains */ }; static PyObject* dictviews_sub(PyObject* self, PyObject *other) { PyObject *result = PySet_New(self); PyObject *tmp; _Py_IDENTIFIER(difference_update); if (result == NULL) return NULL; tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_difference_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } PyObject* _PyDictView_Intersect(PyObject* self, PyObject *other) { PyObject *result = PySet_New(self); PyObject *tmp; _Py_IDENTIFIER(intersection_update); if (result == NULL) return NULL; tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_intersection_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static PyObject* dictviews_or(PyObject* self, PyObject *other) { PyObject *result = PySet_New(self); PyObject *tmp; _Py_IDENTIFIER(update); if (result == NULL) return NULL; tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static PyObject* dictviews_xor(PyObject* self, PyObject *other) { PyObject *result = PySet_New(self); PyObject *tmp; _Py_IDENTIFIER(symmetric_difference_update); if (result == NULL) return NULL; tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_symmetric_difference_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static PyNumberMethods dictviews_as_number = { 0, /*nb_add*/ (binaryfunc)dictviews_sub, /*nb_subtract*/ 0, /*nb_multiply*/ 0, /*nb_remainder*/ 0, /*nb_divmod*/ 0, /*nb_power*/ 0, /*nb_negative*/ 0, /*nb_positive*/ 0, /*nb_absolute*/ 0, /*nb_bool*/ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ (binaryfunc)_PyDictView_Intersect, /*nb_and*/ (binaryfunc)dictviews_xor, /*nb_xor*/ (binaryfunc)dictviews_or, /*nb_or*/ }; static PyObject* dictviews_isdisjoint(PyObject *self, PyObject *other) { PyObject *it; PyObject *item = NULL; if (self == other) { if (dictview_len((_PyDictViewObject *)self) == 0) Py_RETURN_TRUE; else Py_RETURN_FALSE; } /* Iterate over the shorter object (only if other is a set, * because PySequence_Contains may be expensive otherwise): */ if (PyAnySet_Check(other) || PyDictViewSet_Check(other)) { Py_ssize_t len_self = dictview_len((_PyDictViewObject *)self); Py_ssize_t len_other = PyObject_Size(other); if (len_other == -1) return NULL; if ((len_other > len_self)) { PyObject *tmp = other; other = self; self = tmp; } } it = PyObject_GetIter(other); if (it == NULL) return NULL; while ((item = PyIter_Next(it)) != NULL) { int contains = PySequence_Contains(self, item); Py_DECREF(item); if (contains == -1) { Py_DECREF(it); return NULL; } if (contains) { Py_DECREF(it); Py_RETURN_FALSE; } } Py_DECREF(it); if (PyErr_Occurred()) return NULL; /* PyIter_Next raised an exception. */ Py_RETURN_TRUE; } PyDoc_STRVAR(isdisjoint_doc, "Return True if the view and the given iterable have a null intersection."); static PyMethodDef dictkeys_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictKeys_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_keys", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictkeys_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dictkeys_iter, /* tp_iter */ 0, /* tp_iternext */ dictkeys_methods, /* tp_methods */ 0, }; static PyObject * dictkeys_new(PyObject *dict) { return _PyDictView_New(dict, &PyDictKeys_Type); } /*** dict_items ***/ static PyObject * dictitems_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictIterItem_Type); } static int dictitems_contains(_PyDictViewObject *dv, PyObject *obj) { int result; PyObject *key, *value, *found; if (dv->dv_dict == NULL) return 0; if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) return 0; key = PyTuple_GET_ITEM(obj, 0); value = PyTuple_GET_ITEM(obj, 1); found = PyDict_GetItemWithError((PyObject *)dv->dv_dict, key); if (found == NULL) { if (PyErr_Occurred()) return -1; return 0; } Py_INCREF(found); result = PyObject_RichCompareBool(value, found, Py_EQ); Py_DECREF(found); return result; } static PySequenceMethods dictitems_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictitems_contains, /* sq_contains */ }; static PyMethodDef dictitems_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictItems_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_items", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictitems_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dictitems_iter, /* tp_iter */ 0, /* tp_iternext */ dictitems_methods, /* tp_methods */ 0, }; static PyObject * dictitems_new(PyObject *dict) { return _PyDictView_New(dict, &PyDictItems_Type); } /*** dict_values ***/ static PyObject * dictvalues_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictIterValue_Type); } static PySequenceMethods dictvalues_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)0, /* sq_contains */ }; static PyMethodDef dictvalues_methods[] = { {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictValues_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_values", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ (reprfunc)dictview_repr, /* tp_repr */ 0, /* tp_as_number */ &dictvalues_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dictvalues_iter, /* tp_iter */ 0, /* tp_iternext */ dictvalues_methods, /* tp_methods */ 0, }; static PyObject * dictvalues_new(PyObject *dict) { return _PyDictView_New(dict, &PyDictValues_Type); } /* Returns NULL if cannot allocate a new PyDictKeysObject, but does not set an error */ PyDictKeysObject * _PyDict_NewKeysForClass(void) { PyDictKeysObject *keys = new_keys_object(PyDict_MINSIZE); if (keys == NULL) PyErr_Clear(); else keys->dk_lookup = lookdict_split; return keys; } #define CACHED_KEYS(tp) (((PyHeapTypeObject*)tp)->ht_cached_keys) PyObject * PyObject_GenericGetDict(PyObject *obj, void *context) { PyObject *dict, **dictptr = _PyObject_GetDictPtr(obj); if (dictptr == NULL) { PyErr_SetString(PyExc_AttributeError, "This object has no __dict__"); return NULL; } dict = *dictptr; if (dict == NULL) { PyTypeObject *tp = Py_TYPE(obj); if ((tp->tp_flags & Py_TPFLAGS_HEAPTYPE) && CACHED_KEYS(tp)) { DK_INCREF(CACHED_KEYS(tp)); *dictptr = dict = new_dict_with_shared_keys(CACHED_KEYS(tp)); } else { *dictptr = dict = PyDict_New(); } } Py_XINCREF(dict); return dict; } int _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, PyObject *key, PyObject *value) { PyObject *dict; int res; PyDictKeysObject *cached; assert(dictptr != NULL); if ((tp->tp_flags & Py_TPFLAGS_HEAPTYPE) && (cached = CACHED_KEYS(tp))) { assert(dictptr != NULL); dict = *dictptr; if (dict == NULL) { DK_INCREF(cached); dict = new_dict_with_shared_keys(cached); if (dict == NULL) return -1; *dictptr = dict; } if (value == NULL) { res = PyDict_DelItem(dict, key); // Since key sharing dict doesn't allow deletion, PyDict_DelItem() // always converts dict to combined form. if ((cached = CACHED_KEYS(tp)) != NULL) { CACHED_KEYS(tp) = NULL; DK_DECREF(cached); } } else { int was_shared = (cached == ((PyDictObject *)dict)->ma_keys); res = PyDict_SetItem(dict, key, value); if (was_shared && (cached = CACHED_KEYS(tp)) != NULL && cached != ((PyDictObject *)dict)->ma_keys) { /* PyDict_SetItem() may call dictresize and convert split table * into combined table. In such case, convert it to split * table again and update type's shared key only when this is * the only dict sharing key with the type. * * This is to allow using shared key in class like this: * * class C: * def __init__(self): * # one dict resize happens * self.a, self.b, self.c = 1, 2, 3 * self.d, self.e, self.f = 4, 5, 6 * a = C() */ if (cached->dk_refcnt == 1) { CACHED_KEYS(tp) = make_keys_shared(dict); } else { CACHED_KEYS(tp) = NULL; } DK_DECREF(cached); if (CACHED_KEYS(tp) == NULL && PyErr_Occurred()) return -1; } } } else { dict = *dictptr; if (dict == NULL) { dict = PyDict_New(); if (dict == NULL) return -1; *dictptr = dict; } if (value == NULL) { res = PyDict_DelItem(dict, key); } else { res = PyDict_SetItem(dict, key, value); } } return res; } void _PyDictKeys_DecRef(PyDictKeysObject *keys) { DK_DECREF(keys); } python-frozendict/src/frozendict/c_src/3_7/cpython_src/Objects/stringlib/000077500000000000000000000000001470250572200271105ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_7/cpython_src/Objects/stringlib/eq.h000066400000000000000000000015221470250572200276660ustar00rootroot00000000000000/* Fast unicode equal function optimized for dictobject.c and setobject.c */ /* Return 1 if two unicode objects are equal, 0 if not. * unicode_eq() is called when the hash of two unicode objects is equal. */ Py_LOCAL_INLINE(int) unicode_eq(PyObject *aa, PyObject *bb) { assert(PyUnicode_Check(aa)); assert(PyUnicode_Check(bb)); assert(! PyUnicode_READY(aa)); assert(! PyUnicode_READY(bb)); PyUnicodeObject *a = (PyUnicodeObject *)aa; PyUnicodeObject *b = (PyUnicodeObject *)bb; if (PyUnicode_GET_LENGTH(a) != PyUnicode_GET_LENGTH(b)) return 0; if (PyUnicode_GET_LENGTH(a) == 0) return 1; if (PyUnicode_KIND(a) != PyUnicode_KIND(b)) return 0; return memcmp(PyUnicode_1BYTE_DATA(a), PyUnicode_1BYTE_DATA(b), PyUnicode_GET_LENGTH(a) * PyUnicode_KIND(a)) == 0; } python-frozendict/src/frozendict/c_src/3_7/cpython_src/other.c000066400000000000000000000007101470250572200250050ustar00rootroot00000000000000static const unsigned int BitLengthTable[32] = { 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }; unsigned int _Py_bit_length(unsigned long d) { unsigned int d_bits = 0; while (d >= 32) { d_bits += 6; d >>= 6; } d_bits += BitLengthTable[d]; return d_bits; } #define Py_IS_TYPE(op, type) (Py_TYPE(op) == type) #define PySet_CheckExact(op) Py_IS_TYPE(op, &PySet_Type) python-frozendict/src/frozendict/c_src/3_7/frozendictobject.c000066400000000000000000001621561470250572200247040ustar00rootroot00000000000000#include #include "frozendictobject.h" static PyObject* frozendict_iter(PyDictObject *dict); static int frozendict_equal(PyDictObject* a, PyDictObject* b); #include "other.c" #include "dictobject.c" static void frozendict_free_keys_object(PyDictKeysObject *keys, const int decref_items) { if (decref_items) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n; for (i = 0, n = keys->dk_nentries; i < n; i++) { Py_XDECREF(entries[i].me_key); Py_XDECREF(entries[i].me_value); } } #if PyDict_MAXFREELIST > 0 if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) { keys_free_list[numfreekeys++] = keys; return; } #endif PyObject_Free(keys); } static inline void frozendict_keys_decref(PyDictKeysObject *dk, const int decref_items) { assert(dk->dk_refcnt > 0); #ifdef Py_REF_DEBUG _Py_RefTotal--; #endif if (--dk->dk_refcnt == 0) { frozendict_free_keys_object(dk, decref_items); } } static int frozendict_resize(PyDictObject* mp, Py_ssize_t minsize) { const Py_ssize_t newsize = calculate_keysize(minsize); if (newsize <= 0) { PyErr_NoMemory(); return -1; } assert(IS_POWER_OF_2(newsize)); assert(newsize >= PyDict_MINSIZE); PyDictKeysObject* oldkeys = mp->ma_keys; /* Allocate a new table. */ PyDictKeysObject* new_keys = new_keys_object(newsize); if (new_keys == NULL) { return -1; } // New table must be large enough. assert(new_keys->dk_usable >= mp->ma_used); new_keys->dk_lookup = oldkeys->dk_lookup; const Py_ssize_t numentries = mp->ma_used; PyDictKeyEntry* newentries = DK_ENTRIES(new_keys); memcpy( newentries, DK_ENTRIES(oldkeys), numentries * sizeof(PyDictKeyEntry) ); build_indices(new_keys, newentries, numentries); new_keys->dk_usable -= numentries; new_keys->dk_nentries = numentries; // do not decref the keys inside! frozendict_keys_decref(oldkeys, 0); mp->ma_keys = new_keys; return 0; } static int frozendict_insert(PyDictObject *mp, PyObject *key, const Py_hash_t hash, PyObject *value, int empty) { Py_ssize_t ix; PyObject *old_value; PyDictKeysObject* keys = mp->ma_keys; Py_INCREF(key); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); if (! empty) { ix = keys->dk_lookup(mp, key, hash, &old_value); if (ix == DKIX_ERROR) { Py_DECREF(value); Py_DECREF(key); return -1; } empty = (ix == DKIX_EMPTY); } if (empty) { /* Insert into new slot. */ if (mp->ma_keys->dk_usable <= 0) { /* Need to resize. */ if (frozendict_resize(mp, GROWTH_RATE(mp))) { Py_DECREF(value); Py_DECREF(key); return -1; } // resize changes keys keys = mp->ma_keys; } const Py_ssize_t hashpos = find_empty_slot(keys, hash); const Py_ssize_t dk_nentries = keys->dk_nentries; PyDictKeyEntry* ep = &DK_ENTRIES(keys)[dk_nentries]; dictkeys_set_index(keys, hashpos, dk_nentries); ep->me_key = key; ep->me_hash = hash; ep->me_value = value; mp->ma_used++; keys->dk_usable--; keys->dk_nentries++; assert(keys->dk_usable >= 0); } else { DK_ENTRIES(mp->ma_keys)[ix].me_value = value; Py_DECREF(old_value); /* which **CAN** re-enter (see issue #22653) */ Py_DECREF(key); } ASSERT_CONSISTENT(mp); return 0; } static int frozendict_setitem(PyObject *op, PyObject *key, PyObject *value, int empty) { Py_hash_t hash; assert(key); assert(value); if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { return -1; } } return frozendict_insert((PyDictObject*) op, key, hash, value, empty); } // int _PyFrozendict_SetItem(PyObject *op, // PyObject *key, // PyObject *value, // int empty) { // if (! PyAnyFrozenDict_Check(op)) { // PyErr_BadInternalCall(); // return -1; // } // return frozendict_setitem(op, key, value, empty); // } static PyObject* _frozendict_new( PyTypeObject* type, PyObject* args, PyObject* kwds, const int use_empty_frozendict ); static PyObject * frozendict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value) { PyObject *it; /* iter(iterable) */ PyObject *key; PyObject *d; int status; d = _frozendict_new(&PyFrozenDict_Type, NULL, NULL, 0); if (d == NULL) return NULL; Py_ssize_t size; PyDictObject *mp = (PyDictObject *)d; mp->ma_keys = new_keys_object(PyDict_MINSIZE); if (PyAnyDict_CheckExact(iterable)) { PyObject *oldvalue; Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; size = PyDict_GET_SIZE(iterable); if (mp->ma_keys->dk_usable < size) { if (frozendict_resize(mp, estimate_keysize(size))) { Py_DECREF(d); return NULL; } } while (_d_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) { if (frozendict_insert(mp, key, hash, value, 0)) { Py_DECREF(d); return NULL; } } return d; } else if (PyAnySet_CheckExact(iterable)) { Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; size = PySet_GET_SIZE(iterable); if (mp->ma_keys->dk_usable < size) { if (frozendict_resize(mp, estimate_keysize(size))) { Py_DECREF(d); return NULL; } } while (_PySet_NextEntry(iterable, &pos, &key, &hash)) { if (frozendict_insert(mp, key, hash, value, 0)) { Py_DECREF(d); return NULL; } } } else { it = PyObject_GetIter(iterable); if (it == NULL){ Py_DECREF(d); return NULL; } while ((key = PyIter_Next(it)) != NULL) { status = frozendict_setitem(d, key, value, 0); Py_DECREF(key); if (status < 0) { Py_DECREF(it); Py_DECREF(d); return NULL; } } Py_DECREF(it); if (PyErr_Occurred()) { Py_DECREF(d); return NULL; } } ASSERT_CONSISTENT(mp); if (type == &PyFrozenDict_Type) { return d; } PyObject* args = PyTuple_New(1); if (args == NULL) { Py_DECREF(d); return NULL; } PyTuple_SET_ITEM(args, 0, d); PyObject* res = PyObject_Call((PyObject*) type, args, NULL); Py_DECREF(args); return res; } /* Methods */ #define REPR_GENERIC_START "(" #define REPR_GENERIC_END ")" #define REPR_GENERIC_START_LEN 1 #define REPR_GENERIC_END_LEN 1 static PyObject* frozendict_repr(PyFrozenDictObject* mp) { PyObject* dict_repr_res = dict_repr((PyDictObject*) mp); if (dict_repr_res == NULL) { return NULL; } _PyUnicodeWriter writer; _PyUnicodeWriter_Init(&writer); int error = 0; PyObject* o = (PyObject*) mp; Py_ReprEnter(o); PyTypeObject* type = Py_TYPE(mp); size_t frozendict_name_len = strlen(type->tp_name); writer.min_length = ( frozendict_name_len + REPR_GENERIC_START_LEN + PyObject_Length(dict_repr_res) + REPR_GENERIC_END_LEN ); if (_PyUnicodeWriter_WriteASCIIString( &writer, type->tp_name, frozendict_name_len )) { error = 1; } else { if (_PyUnicodeWriter_WriteASCIIString( &writer, REPR_GENERIC_START, REPR_GENERIC_START_LEN )) { error = 1; } else { if (_PyUnicodeWriter_WriteStr(&writer, dict_repr_res)) { error = 1; } else { error = _PyUnicodeWriter_WriteASCIIString( &writer, REPR_GENERIC_END, REPR_GENERIC_END_LEN ); } } } Py_ReprLeave(o); Py_DECREF(dict_repr_res); if (error) { _PyUnicodeWriter_Dealloc(&writer); return NULL; } return _PyUnicodeWriter_Finish(&writer); } static PyMappingMethods frozendict_as_mapping = { (lenfunc)dict_length, /*mp_length*/ (binaryfunc)dict_subscript, /*mp_subscript*/ }; static int frozendict_merge(PyObject* a, PyObject* b, int empty) { /* We accept for the argument either a concrete dictionary object, * or an abstract "mapping" object. For the former, we can do * things quite efficiently. For the latter, we only require that * PyMapping_Keys() and PyObject_GetItem() be supported. */ assert(a != NULL); assert(PyAnyFrozenDict_Check(a)); assert(b != NULL); PyDictObject* mp = (PyDictObject*) a; if ( PyAnyDict_Check(b) && ( Py_TYPE(b)->tp_iter == PyDict_Type.tp_iter || Py_TYPE(b)->tp_iter == (getiterfunc)frozendict_iter ) ) { PyDictObject* other = (PyDictObject*)b; const Py_ssize_t numentries = other->ma_used; if (other == mp || numentries == 0) { /* a.update(a) or a.update({}); nothing to do */ return 0; } const int is_other_combined = other->ma_values == NULL; PyDictKeysObject* okeys = other->ma_keys; if ( empty && is_other_combined && numentries == okeys->dk_nentries ) { PyDictKeysObject *keys = clone_combined_dict_keys(other); if (keys == NULL) { return -1; } mp->ma_keys = keys; mp->ma_used = numentries; mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(mp); if (_PyObject_GC_IS_TRACKED(other) && !_PyObject_GC_IS_TRACKED(mp)) { PyObject_GC_Track(mp); } return 0; } PyDictKeyEntry* ep0 = DK_ENTRIES(okeys); PyDictKeyEntry* entry; PyObject* key; PyObject* value; Py_hash_t hash; int err; if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } /* Do one big resize at the start, rather than * incrementally resizing as we insert new items. Expect * that there will be no (or few) overlapping keys. */ if (mp->ma_keys->dk_usable < numentries) { if (frozendict_resize(mp, estimate_keysize(mp->ma_used + numentries))) { return -1; } } for (Py_ssize_t i = 0, n = okeys->dk_nentries; i < n; i++) { entry = &ep0[i]; key = entry->me_key; hash = entry->me_hash; if (is_other_combined) { value = entry->me_value; } else { value = other->ma_values[i]; } if (value != NULL) { Py_INCREF(key); Py_INCREF(value); err = frozendict_insert(mp, key, hash, value, empty); Py_DECREF(value); Py_DECREF(key); if (err != 0) { return -1; } if (n != other->ma_keys->dk_nentries) { PyErr_SetString(PyExc_RuntimeError, "dict mutated during update"); return -1; } } } } else { /* Do it the generic, slower way */ PyObject *keys = PyMapping_Keys(b); PyObject *iter; PyObject *key, *value; int status; if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } if (keys == NULL) /* Docstring says this is equivalent to E.keys() so * if E doesn't have a .keys() method we want * AttributeError to percolate up. Might as well * do the same for any other error. */ return -1; iter = PyObject_GetIter(keys); Py_DECREF(keys); if (iter == NULL) return -1; for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) { value = PyObject_GetItem(b, key); if (value == NULL) { Py_DECREF(iter); Py_DECREF(key); return -1; } status = frozendict_setitem(a, key, value, 0); Py_DECREF(key); Py_DECREF(value); if (status < 0) { Py_DECREF(iter); return -1; } } Py_DECREF(iter); if (PyErr_Occurred()) /* Iterator completed, via error */ return -1; } ASSERT_CONSISTENT(a); return 0; } static int frozendict_merge_from_seq2(PyObject* d, PyObject* seq2) { assert(d != NULL); assert(PyAnyFrozenDict_Check(d)); assert(seq2 != NULL); PyObject* it = PyObject_GetIter(seq2); if (it == NULL) { return -1; } PyObject* fast; /* item as a 2-tuple or 2-list */ PyObject* key = NULL; PyObject* value = NULL; Py_ssize_t n; PyObject* item; int res = 0; PyDictObject* mp = (PyDictObject*) d; if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } for (Py_ssize_t i = 0; ; ++i) { fast = NULL; item = PyIter_Next(it); if (item == NULL) { if (PyErr_Occurred()) { res = -1; } break; } /* Convert item to sequence, and verify length 2. */ fast = PySequence_Fast(item, ""); if (fast == NULL) { if (PyErr_ExceptionMatches(PyExc_TypeError)) { PyErr_Format(PyExc_TypeError, "cannot convert dictionary update " "sequence element #%zd to a sequence", i); } Py_DECREF(item); res = -1; break; } n = PySequence_Fast_GET_SIZE(fast); if (n != 2) { PyErr_Format(PyExc_ValueError, "dictionary update sequence element #%zd " "has length %zd; 2 is required", i, n); Py_DECREF(fast); Py_DECREF(item); res = -1; break; } /* Update/merge with this (key, value) pair. */ key = PySequence_Fast_GET_ITEM(fast, 0); Py_INCREF(key); value = PySequence_Fast_GET_ITEM(fast, 1); Py_INCREF(value); if (frozendict_setitem(d, key, value, 0) < 0) { Py_DECREF(key); Py_DECREF(value); Py_DECREF(fast); Py_DECREF(item); res = -1; break; } Py_DECREF(key); Py_DECREF(value); Py_DECREF(fast); Py_DECREF(item); } Py_DECREF(it); ASSERT_CONSISTENT(d); return res; } static int frozendict_update_arg(PyObject *self, PyObject *arg, const int empty) { if (PyAnyDict_CheckExact(arg)) { return frozendict_merge(self, arg, empty); } _Py_IDENTIFIER(keys); PyObject *func; if (_PyObject_LookupAttrId(arg, &PyId_keys, &func) < 0) { return -1; } if (func != NULL) { Py_DECREF(func); return frozendict_merge(self, arg, empty); } return frozendict_merge_from_seq2(self, arg); } static int frozendict_update_common(PyObject* self, PyObject* arg, PyObject* kwds) { int result = 0; const int no_arg = (arg == NULL); if (! no_arg) { result = frozendict_update_arg(self, arg, 1); } if (result == 0 && kwds != NULL) { if (PyArg_ValidateKeywordArguments(kwds)) { result = frozendict_merge(self, kwds, no_arg); } else { result = -1; } } return result; } /* Forward */ static PyObject *frozendictkeys_new(PyObject *, PyObject *); static PyObject *frozendictitems_new(PyObject *, PyObject *); static PyObject *frozendictvalues_new(PyObject *, PyObject *); #define MINUSONE_HASH ((Py_hash_t) -1) static Py_hash_t frozendict_hash(PyObject* self) { PyFrozenDictObject* frozen_self = (PyFrozenDictObject*) self; Py_hash_t hash; if (frozen_self->ma_hash != MINUSONE_HASH) { hash = frozen_self->ma_hash; } else { PyObject* frozen_items_tmp = frozendictitems_new(self, NULL); int save_hash = 1; if (frozen_items_tmp == NULL) { hash = MINUSONE_HASH; save_hash = 0; } else { PyObject* frozen_items = PyFrozenSet_New(frozen_items_tmp); Py_DECREF(frozen_items_tmp); if (frozen_items == NULL) { hash = MINUSONE_HASH; save_hash = 0; } else { hash = PyFrozenSet_Type.tp_hash(frozen_items); Py_DECREF(frozen_items); } } if (save_hash) { frozen_self->ma_hash = hash; } } return hash; } static PyObject* frozendict_copy(PyObject* o, PyObject* Py_UNUSED(ignored)) { if (PyAnyFrozenDict_CheckExact(o)) { Py_INCREF(o); return o; } PyObject* args = PyTuple_New(1); if (args == NULL) { return NULL; } Py_INCREF(o); PyTuple_SET_ITEM(args, 0, o); PyTypeObject* type = Py_TYPE(o); PyObject* res = PyObject_Call((PyObject *) type, args, NULL); Py_DECREF(args); return res; } PyObject* frozendict_deepcopy(PyObject* self, PyObject* memo) { if (PyAnyFrozenDict_CheckExact(self)) { frozendict_hash(self); if (PyErr_Occurred()) { PyErr_Clear(); } else { Py_INCREF(self); return self; } } if (! PyAnyFrozenDict_Check(self)) { Py_RETURN_NOTIMPLEMENTED; } PyObject* d = PyDict_New(); if (d == NULL) { return NULL; } PyObject* copy_module_name = NULL; PyObject* copy_module = NULL; PyObject* deepcopy_fun = NULL; PyObject* deep_args = NULL; PyObject* deep_d = NULL; PyObject* args = NULL; PyObject* res = NULL; int decref_d = 1; int decref_deep_d = 1; if (PyDict_Merge(d, self, 1)) { goto end; } copy_module_name = PyUnicode_FromString("copy"); if (copy_module_name == NULL) { goto end; } copy_module = PyImport_Import(copy_module_name); if (copy_module == NULL) { goto end; } deepcopy_fun = PyObject_GetAttrString(copy_module, "deepcopy"); if (deepcopy_fun == NULL) { goto end; } deep_args = PyTuple_New(2); if (deep_args == NULL) { goto end; } PyTuple_SET_ITEM(deep_args, 0, d); decref_d = 0; Py_INCREF(memo); PyTuple_SET_ITEM(deep_args, 1, memo); deep_d = PyObject_CallObject(deepcopy_fun, deep_args); if (deep_d == NULL) { goto end; } args = PyTuple_New(1); if (args == NULL) { goto end; } PyTuple_SET_ITEM(args, 0, deep_d); decref_deep_d = 0; PyTypeObject* type = Py_TYPE(self); res = PyObject_Call((PyObject *) type, args, NULL); end: Py_XDECREF(args); Py_XDECREF(deep_args); Py_XDECREF(deepcopy_fun); Py_XDECREF(copy_module); Py_XDECREF(copy_module_name); if (decref_d) { Py_DECREF(d); } if (decref_deep_d) { Py_DECREF(deep_d); } return res; } static int frozendict_equal(PyDictObject* a, PyDictObject* b) { if (a == b) { return 1; } if (a->ma_used != b->ma_used) { /* can't be equal if # of entries differ */ return 0; } PyDictKeysObject* keys = a->ma_keys; PyDictKeyEntry* ep; PyObject* aval; int cmp = 1; PyObject* bval; PyObject* key; /* Same # of entries -- check all of 'em. Exit early on any diff. */ for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) { ep = &DK_ENTRIES(keys)[i]; aval = ep->me_value; Py_INCREF(aval); key = ep->me_key; Py_INCREF(key); /* reuse the known hash value */ b->ma_keys->dk_lookup(b, key, ep->me_hash, &bval); if (bval == NULL) { if (PyErr_Occurred()) { cmp = -1; } else { cmp = 0; } } else { Py_INCREF(bval); cmp = PyObject_RichCompareBool(aval, bval, Py_EQ); Py_DECREF(bval); } Py_DECREF(key); Py_DECREF(aval); if (cmp <= 0) { /* error or not equal */ break; } } return cmp; } static Py_ssize_t dict_get_index(PyDictObject *self, PyObject *key) { Py_hash_t hash; PyObject* val; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return DKIX_ERROR; } return (self->ma_keys->dk_lookup) (self, key, hash, &val); } static PyObject * frozendict_reduce(PyFrozenDictObject* mp, PyObject *Py_UNUSED(ignored)) { PyObject *d = PyDict_New(); if (d == NULL) { return NULL; } if (PyDict_Merge(d, (PyObject *)mp, 1)) { Py_DECREF(d); return NULL; } return Py_BuildValue("O(N)", Py_TYPE(mp), d); } static PyObject* frozendict_clone(PyObject* self) { PyTypeObject* type = Py_TYPE(self); PyObject* new_op = type->tp_alloc(type, 0); if (new_op == NULL){ return NULL; } if (type == &PyFrozenDict_Type) { PyObject_GC_UnTrack(new_op); } PyDictObject* mp = (PyDictObject*) self; PyDictKeysObject *keys = clone_combined_dict_keys(mp); if (keys == NULL) { return NULL; } PyFrozenDictObject* new_mp = (PyFrozenDictObject*) new_op; new_mp->ma_keys = keys; if (_PyObject_GC_IS_TRACKED(mp) && !_PyObject_GC_IS_TRACKED(new_mp)) { PyObject_GC_Track(new_mp); } new_mp->ma_used = mp->ma_used; new_mp->ma_hash = MINUSONE_HASH; new_mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(new_mp); return new_op; } static PyObject* frozendict_set( PyObject* self, PyObject* const* args, Py_ssize_t nargs ) { PyObject* set_key; PyObject* set_val; if (!_PyArg_UnpackStack(args, nargs, "set", 2, 2, &set_key, &set_val)) { return NULL; } PyObject* new_op = frozendict_clone(self); if (new_op == NULL) { return NULL; } if (frozendict_setitem(new_op, set_key, set_val, 0)) { Py_DECREF(new_op); return NULL; } if ( ((PyDictObject*) self)->ma_keys->dk_lookup == lookdict_unicode_nodummy && ! PyUnicode_CheckExact(set_key) ) { ((PyFrozenDictObject*) new_op)->ma_keys->dk_lookup = lookdict; } return new_op; } static PyObject* frozendict_setdefault( PyObject* self, PyObject* const* args, Py_ssize_t nargs ) { PyObject* set_key; PyObject* val = Py_None; if (!_PyArg_UnpackStack(args, nargs, "setdefault", 1, 2, &set_key, &val)) { return NULL; } if (PyDict_Contains(self, set_key)) { Py_INCREF(self); return self; } PyObject* new_op = frozendict_clone(self); if (new_op == NULL) { return NULL; } if (frozendict_setitem(new_op, set_key, val, 0)) { Py_DECREF(new_op); return NULL; } if ( ((PyDictObject*) self)->ma_keys->dk_lookup == lookdict_unicode_nodummy && ! PyUnicode_CheckExact(set_key) ) { ((PyFrozenDictObject*) new_op)->ma_keys->dk_lookup = lookdict; } return new_op; } static PyObject* frozendict_delete( PyObject* self, PyObject *const *args, Py_ssize_t nargs ) { PyObject* del_key; if (!_PyArg_UnpackStack(args, nargs, "delete", 1, 1, &del_key)) { return NULL; } PyDictObject* mp = (PyDictObject*) self; const Py_ssize_t ix = dict_get_index(mp, del_key); if (ix == DKIX_ERROR) { return NULL; } if (ix == DKIX_EMPTY) { _PyErr_SetKeyError(del_key); return NULL; } const Py_ssize_t size = mp->ma_used; const Py_ssize_t sizemm = size - 1; if (sizemm == 0) { PyObject* args = PyTuple_New(0); if (args == NULL) { return NULL; } return PyObject_Call((PyObject*) Py_TYPE(self), args, NULL); } PyTypeObject* type = Py_TYPE(self); PyObject* new_op = type->tp_alloc(type, 0); if (new_op == NULL) { return NULL; } if (type == &PyFrozenDict_Type) { PyObject_GC_UnTrack(new_op); } const Py_ssize_t newsize = estimate_keysize(sizemm); if (newsize <= 0) { Py_DECREF(new_op); PyErr_NoMemory(); return NULL; } assert(IS_POWER_OF_2(newsize)); assert(newsize >= PyDict_MINSIZE); /* Allocate a new table. */ PyDictKeysObject* new_keys = new_keys_object(newsize); if (new_keys == NULL) { Py_DECREF(new_op); return NULL; } const PyDictKeysObject* old_keys = mp->ma_keys; new_keys->dk_lookup = old_keys->dk_lookup; PyFrozenDictObject* new_mp = (PyFrozenDictObject*) new_op; // New table must be large enough. assert(new_keys->dk_usable >= new_mp->ma_used); new_mp->ma_keys = new_keys; new_mp->ma_hash = MINUSONE_HASH; new_mp->ma_version_tag = DICT_NEXT_VERSION(); PyObject* key; PyObject* value; Py_hash_t hash; Py_ssize_t hashpos; PyDictKeyEntry* old_entries = DK_ENTRIES(old_keys); PyDictKeyEntry* new_entries = DK_ENTRIES(new_keys); PyDictKeyEntry* old_entry; PyDictKeyEntry* new_entry; Py_ssize_t new_i; int deleted = 0; for (Py_ssize_t i = 0; i < size; i++) { if (i == ix) { deleted = 1; continue; } new_i = i - deleted; old_entry = &old_entries[i]; hash = old_entry->me_hash; key = old_entry->me_key; value = old_entry->me_value; Py_INCREF(key); Py_INCREF(value); hashpos = find_empty_slot(new_keys, hash); dictkeys_set_index(new_keys, hashpos, new_i); new_entry = &new_entries[new_i]; new_entry->me_key = key; new_entry->me_hash = hash; new_entry->me_value = value; } new_mp->ma_used = sizemm; new_keys->dk_usable -= sizemm; new_keys->dk_nentries = sizemm; ASSERT_CONSISTENT(new_mp); return new_op; } static const PyObject* frozendict_key( PyObject* self, PyObject *const *args, Py_ssize_t nargs ) { PyObject* i; if (!_PyArg_UnpackStack(args, nargs, "key", 0, 1, &i)) { return NULL; } Py_ssize_t index; Py_ssize_t passed_index; PyDictObject* d = (PyDictObject*) self; const Py_ssize_t size = d->ma_used; if (nargs > 0) { index = PyLong_AsSsize_t(i); passed_index = index; if (index < 0) { if (PyErr_Occurred()) { return NULL; } index += size; } } else { index = 0; passed_index = index; } const Py_ssize_t maxindex = size - 1; if (index > maxindex || index < 0) { PyErr_Format( PyExc_IndexError, "%s index %zd out of range %zd", Py_TYPE(self)->tp_name, passed_index, maxindex ); return NULL; } const PyObject* res = DK_ENTRIES(d->ma_keys)[index].me_key; Py_INCREF(res); return res; } static const PyObject* frozendict_value( PyObject* self, PyObject *const *args, Py_ssize_t nargs ) { PyObject* i; if (!_PyArg_UnpackStack(args, nargs, "value", 0, 1, &i)) { return NULL; } Py_ssize_t index; Py_ssize_t passed_index; PyDictObject* d = (PyDictObject*) self; const Py_ssize_t size = d->ma_used; if (nargs > 0) { index = PyLong_AsSsize_t(i); passed_index = index; if (index < 0) { if (PyErr_Occurred()) { return NULL; } index += size; } } else { index = 0; passed_index = index; } const Py_ssize_t maxindex = size - 1; if (index > maxindex || index < 0) { PyErr_Format( PyExc_IndexError, "%s index %zd out of range %zd", Py_TYPE(self)->tp_name, passed_index, maxindex ); return NULL; } const PyObject* res = DK_ENTRIES(d->ma_keys)[index].me_value; Py_INCREF(res); return res; } static const PyObject* frozendict_item( PyObject* self, PyObject *const *args, Py_ssize_t nargs ) { PyObject* i; if (!_PyArg_UnpackStack(args, nargs, "item", 0, 1, &i)) { return NULL; } Py_ssize_t index; Py_ssize_t passed_index; PyDictObject* d = (PyDictObject*) self; const Py_ssize_t size = d->ma_used; if (nargs > 0) { index = PyLong_AsSsize_t(i); passed_index = index; if (index < 0) { if (PyErr_Occurred()) { return NULL; } index += size; } } else { index = 0; passed_index = index; } const Py_ssize_t maxindex = size - 1; if (index > maxindex || index < 0) { PyErr_Format( PyExc_IndexError, "%s index %zd out of range %zd", Py_TYPE(self)->tp_name, passed_index, maxindex ); return NULL; } PyObject* key = DK_ENTRIES(d->ma_keys)[index].me_key; Py_INCREF(key); PyObject* val = DK_ENTRIES(d->ma_keys)[index].me_value; Py_INCREF(val); const PyObject* res = PyTuple_New(2); PyTuple_SET_ITEM(res, 0, key); PyTuple_SET_ITEM(res, 1, val); return res; } PyDoc_STRVAR(frozendict_set_doc, "set($self, key, value, /)\n" "--\n" "\n" "Returns a copy of the dictionary with the new (key, value) item. "); PyDoc_STRVAR(frozendict_setdefault_doc, "set($self, key[, default], /)\n" "--\n" "\n" "If key is in the dictionary, it returns the dictionary unchanged. \n" "Otherwise, it returns a copy of the dictionary with the new (key, default) item; \n" "default argument is optional and is None by default. "); PyDoc_STRVAR(frozendict_delete_doc, "delete($self, key, /)\n" "--\n" "\n" "Returns a copy of the dictionary without the item of the corresponding key. "); PyDoc_STRVAR(frozendict_key_doc, "key($self[, index], /)\n" "--\n" "\n" "Get the key at the specified index (insertion order). If index is not \n" "passed, it defaults to 0. If index is negative, returns the key at \n" "position size + index. "); PyDoc_STRVAR(frozendict_value_doc, "value($self[, index], /)\n" "--\n" "\n" "Get the value at the specified index (insertion order). If index is not \n" "passed, it defaults to 0. If index is negative, returns the value at \n" "position size + index. "); PyDoc_STRVAR(frozendict_item_doc, "item($self[, index], /)\n" "--\n" "\n" "Get the (key, value) item at the specified index (insertion order). If \n" "index is not passed, it defaults to 0. If index is negative, returns the \n" "item at position size + index. "); static PyMethodDef frozendict_mapp_methods[] = { DICT___CONTAINS___METHODDEF {"__getitem__", (PyCFunction)(void(*)(void))dict_subscript, METH_O | METH_COEXIST, getitem__doc__}, {"__sizeof__", (PyCFunction)(void(*)(void))dict_sizeof, METH_NOARGS, sizeof__doc__}, DICT_GET_METHODDEF {"keys", frozendictkeys_new, METH_NOARGS, keys__doc__}, {"items", frozendictitems_new, METH_NOARGS, items__doc__}, {"values", frozendictvalues_new, METH_NOARGS, values__doc__}, {"fromkeys", (PyCFunction)(void(*)(void))dict_fromkeys, METH_FASTCALL|METH_CLASS, dict_fromkeys__doc__}, {"copy", (PyCFunction)frozendict_copy, METH_NOARGS, copy__doc__}, {"__copy__", (PyCFunction)frozendict_copy, METH_NOARGS, "Returns a copy of the object."}, {"__deepcopy__", (PyCFunction)frozendict_deepcopy, METH_O, "Returns a deepcopy of the object."}, DICT___REVERSED___METHODDEF {"__reduce__", (PyCFunction)(void(*)(void))frozendict_reduce, METH_NOARGS, ""}, {"set", (PyCFunction)(void(*)(void)) frozendict_set, METH_FASTCALL, frozendict_set_doc}, {"setdefault", (PyCFunction)(void(*)(void)) frozendict_setdefault, METH_FASTCALL, frozendict_setdefault_doc}, {"delete", (PyCFunction)(void(*)(void)) frozendict_delete, METH_FASTCALL, frozendict_delete_doc}, {"key", (PyCFunction)(void(*)(void)) frozendict_key, METH_FASTCALL, frozendict_key_doc}, {"value", (PyCFunction)(void(*)(void)) frozendict_value, METH_FASTCALL, frozendict_value_doc}, {"item", (PyCFunction)(void(*)(void)) frozendict_item, METH_FASTCALL, frozendict_item_doc}, {NULL, NULL} /* sentinel */ }; static PyObject* frozendict_new_barebone(PyTypeObject* type) { PyObject* self = type->tp_alloc(type, 0); if (self == NULL) { return NULL; } /* The object has been implicitly tracked by tp_alloc */ if (type == &PyFrozenDict_Type) { PyObject_GC_UnTrack(self); } PyFrozenDictObject* mp = (PyFrozenDictObject*) self; mp->ma_keys = NULL; mp->ma_values = NULL; mp->ma_used = 0; mp->ma_hash = MINUSONE_HASH; return self; } // empty frozendict singleton static PyObject* empty_frozendict = NULL; // if frozendict is empty, return the empty singleton static PyObject* frozendict_create_empty( PyFrozenDictObject* mp, const PyTypeObject* type, const int use_empty_frozendict ) { if (mp->ma_used == 0) { if (use_empty_frozendict && type == &PyFrozenDict_Type) { if (empty_frozendict == NULL) { empty_frozendict = (PyObject*) mp; Py_INCREF(Py_EMPTY_KEYS); ((PyDictObject*) empty_frozendict)->ma_keys = Py_EMPTY_KEYS; mp->ma_version_tag = DICT_NEXT_VERSION(); } else { Py_DECREF(mp); } Py_INCREF(empty_frozendict); return empty_frozendict; } else { if (mp->ma_keys != NULL) { frozendict_keys_decref(mp->ma_keys, 0); } Py_INCREF(Py_EMPTY_KEYS); mp->ma_keys = Py_EMPTY_KEYS; return NULL; } } return NULL; } static PyObject* _frozendict_new( PyTypeObject* type, PyObject* args, PyObject* kwds, const int use_empty_frozendict ) { assert(type != NULL && type->tp_alloc != NULL); PyObject* arg = NULL; if (args != NULL && ! PyArg_UnpackTuple(args, "dict", 0, 1, &arg)) { return NULL; } const int arg_is_frozendict = (arg != NULL && PyAnyFrozenDict_CheckExact(arg)); const int kwds_size = ((kwds != NULL) ? ((PyDictObject*) kwds)->ma_used : 0 ); // only argument is a frozendict if (arg_is_frozendict && kwds_size == 0 && type == &PyFrozenDict_Type) { Py_INCREF(arg); return arg; } PyObject* self = frozendict_new_barebone(type); PyFrozenDictObject* mp = (PyFrozenDictObject*) self; if (frozendict_update_common(self, arg, kwds)) { Py_DECREF(self); return NULL; } PyObject* empty = frozendict_create_empty(mp, type, use_empty_frozendict); if (empty != NULL) { return empty; } mp->ma_version_tag = DICT_NEXT_VERSION(); return self; } static PyObject* frozendict_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { return _frozendict_new(type, args, kwds, 1); } static PyObject* frozendict_or(PyObject *self, PyObject *other) { if (! PyAnyFrozenDict_Check(self) || ! PyAnyDict_Check(other)) { Py_RETURN_NOTIMPLEMENTED; } PyObject* new = frozendict_clone(self); if (new == NULL) { return NULL; } if (frozendict_update_arg(new, other, 0)) { Py_DECREF(new); return NULL; } return new; } static PyNumberMethods frozendict_as_number = { .nb_or = frozendict_or, }; #define FROZENDICT_CLASS_NAME "frozendict" #define FROZENDICT_MODULE_NAME "frozendict" #define FROZENDICT_FULL_NAME FROZENDICT_MODULE_NAME "." FROZENDICT_CLASS_NAME PyDoc_STRVAR(frozendict_doc, "An immutable version of dict.\n" "\n" FROZENDICT_FULL_NAME "() -> returns an empty immutable dictionary\n" FROZENDICT_FULL_NAME "(mapping) -> returns an immutable dictionary initialized from a mapping object's\n" " (key, value) pairs\n" FROZENDICT_FULL_NAME "(iterable) -> returns an immutable dictionary, equivalent to:\n" " d = {}\n" " " " for k, v in iterable:\n" " d[k] = v\n" " " " " FROZENDICT_FULL_NAME "(d)\n" FROZENDICT_FULL_NAME "(**kwargs) -> returns an immutable dictionary initialized with the name=value pairs\n" " in the keyword argument list. For example: " FROZENDICT_FULL_NAME "(one=1, two=2)"); static PyTypeObject PyFrozenDict_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_FULL_NAME, /* tp_name */ sizeof(PyFrozenDictObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)dict_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)frozendict_repr, /* tp_repr */ &frozendict_as_number, /* tp_as_number */ &dict_as_sequence, /* tp_as_sequence */ &frozendict_as_mapping, /* tp_as_mapping */ (hashfunc)frozendict_hash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ frozendict_doc, /* tp_doc */ dict_traverse, /* tp_traverse */ 0, /* tp_clear */ dict_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendict_iter, /* tp_iter */ 0, /* tp_iternext */ frozendict_mapp_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ frozendict_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ }; /* Dictionary iterator types */ static PyObject* frozendict_iter(PyDictObject *dict) { return dictiter_new(dict, &PyFrozenDictIterKey_Type); } static PyObject* frozendictiter_iternextkey(dictiterobject* di) { Py_ssize_t pos = di->di_pos; assert(pos >= 0); PyDictObject* d = di->di_dict; if (d == NULL) { return NULL; } assert(PyAnyFrozenDict_Check(d)); if (pos >= d->ma_used) { di->di_dict = NULL; Py_DECREF(d); return NULL; } PyObject* key = DK_ENTRIES(d->ma_keys)[pos].me_key; assert(key != NULL); di->di_pos++; di->len--; Py_INCREF(key); return key; } static PyTypeObject PyFrozenDictIterKey_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".keyiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)frozendictiter_iternextkey, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject* frozendictiter_iternextvalue(dictiterobject* di) { Py_ssize_t pos = di->di_pos; assert(pos >= 0); PyDictObject* d = di->di_dict; if (d == NULL) { return NULL; } assert(PyAnyFrozenDict_Check(d)); if (pos >= d->ma_used) { di->di_dict = NULL; Py_DECREF(d); return NULL; } PyObject* val = DK_ENTRIES(d->ma_keys)[pos].me_value; assert(val != NULL); di->di_pos++; di->len--; Py_INCREF(val); return val; } static PyTypeObject PyFrozenDictIterValue_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".valueiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)frozendictiter_iternextvalue, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject* frozendictiter_iternextitem(dictiterobject* di) { Py_ssize_t pos = di->di_pos; assert(pos >= 0); PyDictObject* d = di->di_dict; if (d == NULL) { return NULL; } assert(PyAnyFrozenDict_Check(d)); if (pos >= d->ma_used) { di->di_dict = NULL; Py_DECREF(d); return NULL; } PyDictKeyEntry* entry_ptr = &DK_ENTRIES(d->ma_keys)[pos]; PyObject* key = entry_ptr->me_key; PyObject* val = entry_ptr->me_value; assert(key != NULL); assert(val != NULL); di->di_pos++; di->len--; Py_INCREF(key); Py_INCREF(val); PyObject* result; if (Py_REFCNT(di->di_result) == 1) { result = di->di_result; PyObject *oldkey = PyTuple_GET_ITEM(result, 0); PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); if (!_PyObject_GC_IS_TRACKED(result)) { PyObject_GC_Track(result); } } else { result = PyTuple_New(2); if (result == NULL) return NULL; } PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, val); /* steals reference */ return result; } static PyTypeObject PyFrozenDictIterItem_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".itemiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)frozendictiter_iternextitem, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; /*** dict_keys ***/ static PyObject * frozendictkeys_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyFrozenDictIterKey_Type); } static PyTypeObject PyFrozenDictKeys_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".keys", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictkeys_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendictkeys_iter, /* tp_iter */ 0, /* tp_iternext */ dictkeys_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * frozendictkeys_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _d_PyDictView_New(dict, &PyFrozenDictKeys_Type); } /*** dict_items ***/ static PyObject * frozendictitems_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyFrozenDictIterItem_Type); } static PyTypeObject PyFrozenDictItems_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".items", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictitems_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendictitems_iter, /* tp_iter */ 0, /* tp_iternext */ dictitems_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * frozendictitems_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _d_PyDictView_New(dict, &PyFrozenDictItems_Type); } /*** dict_values ***/ static PyObject * frozendictvalues_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyFrozenDictIterValue_Type); } static PyTypeObject PyFrozenDictValues_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".values", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ 0, /* tp_as_number */ &dictvalues_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendictvalues_iter, /* tp_iter */ 0, /* tp_iternext */ dictvalues_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * frozendictvalues_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _d_PyDictView_New(dict, &PyFrozenDictValues_Type); } static int frozendict_exec(PyObject *m) { /* Finalize the type object including setting type of the new type * object; doing it here is required for portability, too. */ if (PyType_Ready(&PyFrozenDict_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictIterKey_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictIterValue_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictIterItem_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictKeys_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictItems_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictValues_Type) < 0) { goto fail; } if (PyType_Ready(&PyDictRevIterKey_Type) < 0) { goto fail; } if (PyType_Ready(&PyDictRevIterItem_Type) < 0) { goto fail; } if (PyType_Ready(&PyDictRevIterValue_Type) < 0) { goto fail; } PyModule_AddObject(m, FROZENDICT_CLASS_NAME, (PyObject *)&PyFrozenDict_Type); return 0; fail: Py_XDECREF(m); return -1; } static struct PyModuleDef_Slot frozendict_slots[] = { {Py_mod_exec, frozendict_exec}, {0, NULL}, }; static struct PyModuleDef frozendictmodule = { PyModuleDef_HEAD_INIT, FROZENDICT_MODULE_NAME, /* name of module */ NULL, /* module documentation, may be NULL */ 0, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ NULL, frozendict_slots, NULL, NULL, NULL }; PyMODINIT_FUNC PyInit__frozendict(void) { return PyModuleDef_Init(&frozendictmodule); } python-frozendict/src/frozendict/c_src/3_8/000077500000000000000000000000001470250572200211705ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_8/Include/000077500000000000000000000000001470250572200225535ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_8/Include/cpython/000077500000000000000000000000001470250572200242375ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_8/Include/cpython/frozendictobject.h000066400000000000000000000004601470250572200277460ustar00rootroot00000000000000#ifndef Py_CPYTHON_FROZENDICTOBJECT_H # error "this header file must not be included directly" #endif typedef struct { PyObject_HEAD Py_ssize_t ma_used; uint64_t ma_version_tag; PyDictKeysObject* ma_keys; PyObject** ma_values; Py_hash_t ma_hash; } PyFrozenDictObject; python-frozendict/src/frozendict/c_src/3_8/Include/frozendictobject.h000066400000000000000000000050501470250572200262620ustar00rootroot00000000000000#ifndef Py_FROZENDICTOBJECT_H #define Py_FROZENDICTOBJECT_H #ifdef __cplusplus extern "C" { #endif // PyAPI_DATA(PyTypeObject) PyFrozenDict_Type; static PyTypeObject PyFrozenDict_Type; // PyAPI_DATA(PyTypeObject) PyCoold_Type; static PyTypeObject PyCoold_Type; #define PyFrozenDict_Check(op) \ ( \ Py_IS_TYPE(op, &PyFrozenDict_Type) \ || PyType_IsSubtype(Py_TYPE(op), &PyFrozenDict_Type) \ ) #define PyFrozenDict_CheckExact(op) Py_IS_TYPE(op, &PyFrozenDict_Type) #define PyAnyFrozenDict_Check(op) \ ( \ Py_IS_TYPE(op, &PyFrozenDict_Type) \ || Py_IS_TYPE(op, &PyCoold_Type) \ || PyType_IsSubtype(Py_TYPE(op), &PyFrozenDict_Type) \ || PyType_IsSubtype(Py_TYPE(op), &PyCoold_Type) \ ) #define PyAnyFrozenDict_CheckExact(op) \ ( \ Py_IS_TYPE(op, &PyFrozenDict_Type) \ || Py_IS_TYPE(op, &PyCoold_Type) \ ) #define PyAnyDict_Check(ob) \ ( \ PyDict_Check(ob) \ || Py_IS_TYPE(ob, &PyFrozenDict_Type) \ || Py_IS_TYPE(ob, &PyCoold_Type) \ || PyType_IsSubtype(Py_TYPE(ob), &PyFrozenDict_Type) \ || PyType_IsSubtype(Py_TYPE(ob), &PyCoold_Type) \ ) #define PyAnyDict_CheckExact(op) ( \ (Py_IS_TYPE(op, &PyDict_Type)) \ || (Py_IS_TYPE(op, &PyFrozenDict_Type)) \ || (Py_IS_TYPE(op, &PyCoold_Type)) \ ) // PyAPI_DATA(PyTypeObject) PyFrozenDictIterKey_Type; static PyTypeObject PyFrozenDictIterKey_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictIterValues_Type; static PyTypeObject PyFrozenDictIterValue_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictIterItem_Type; static PyTypeObject PyFrozenDictIterItem_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictKeys_Type; static PyTypeObject PyFrozenDictKeys_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictValues_Type; static PyTypeObject PyFrozenDictValues_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictItems_Type; static PyTypeObject PyFrozenDictItems_Type; #define PyAnyDictKeys_Check(op) (PyDictKeys_Check(op) || PyObject_TypeCheck(op, &PyFrozenDictKeys_Type)) #define PyAnyDictValues_Check(op) (PyDictValues_Check(op) || PyObject_TypeCheck(op, &PyFrozenDictValues_Type)) #define PyAnyDictItems_Check(op) (PyDictItems_Check(op) || PyObject_TypeCheck(op, &PyFrozenDictItems_Type)) /* This excludes Values, since they are not sets. */ # define PyAnyDictViewSet_Check(op) \ (PyAnyDictKeys_Check(op) || PyAnyDictItems_Check(op)) #ifndef Py_LIMITED_API # define Py_CPYTHON_FROZENDICTOBJECT_H # include "cpython/frozendictobject.h" # undef Py_CPYTHON_FROZENDICTOBJECT_H #endif #ifdef __cplusplus } #endif #endif python-frozendict/src/frozendict/c_src/3_8/cpython_src/000077500000000000000000000000001470250572200235235ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_8/cpython_src/Objects/000077500000000000000000000000001470250572200251145ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_8/cpython_src/Objects/clinic/000077500000000000000000000000001470250572200263555ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_8/cpython_src/Objects/clinic/dictobject.c.h000066400000000000000000000051111470250572200310570ustar00rootroot00000000000000/*[clinic input] preserve [clinic start generated code]*/ PyDoc_STRVAR(dict_fromkeys__doc__, "fromkeys($type, iterable, value=None, /)\n" "--\n" "\n" "Create a new dictionary with keys from iterable and values set to value."); #define DICT_FROMKEYS_METHODDEF \ {"fromkeys", (PyCFunction)(void(*)(void))dict_fromkeys, METH_FASTCALL|METH_CLASS, dict_fromkeys__doc__}, static PyObject * frozendict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value); static PyObject * dict_fromkeys(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; PyObject *iterable; PyObject *value = Py_None; if (!_PyArg_CheckPositional("fromkeys", nargs, 1, 2)) { goto exit; } iterable = args[0]; if (nargs < 2) { goto skip_optional; } value = args[1]; skip_optional: return_value = frozendict_fromkeys_impl(type, iterable, value); exit: return return_value; } PyDoc_STRVAR(dict___contains____doc__, "__contains__($self, key, /)\n" "--\n" "\n" "True if the dictionary has the specified key, else False."); #define DICT___CONTAINS___METHODDEF \ {"__contains__", (PyCFunction)dict___contains__, METH_O|METH_COEXIST, dict___contains____doc__}, PyDoc_STRVAR(dict_get__doc__, "get($self, key, default=None, /)\n" "--\n" "\n" "Return the value for key if key is in the dictionary, else default."); #define DICT_GET_METHODDEF \ {"get", (PyCFunction)(void(*)(void))dict_get, METH_FASTCALL, dict_get__doc__}, static PyObject * dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value); static PyObject * dict_get(PyDictObject *self, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; PyObject *key; PyObject *default_value = Py_None; if (!_PyArg_CheckPositional("get", nargs, 1, 2)) { goto exit; } key = args[0]; if (nargs < 2) { goto skip_optional; } default_value = args[1]; skip_optional: return_value = dict_get_impl(self, key, default_value); exit: return return_value; } PyDoc_STRVAR(dict___reversed____doc__, "__reversed__($self, /)\n" "--\n" "\n" "Return a reverse iterator over the dict keys."); #define DICT___REVERSED___METHODDEF \ {"__reversed__", (PyCFunction)dict___reversed__, METH_NOARGS, dict___reversed____doc__}, static PyObject * dict___reversed___impl(PyDictObject *self); static PyObject * dict___reversed__(PyDictObject *self, PyObject *Py_UNUSED(ignored)) { return dict___reversed___impl(self); } /*[clinic end generated code: output=7b77c16e43d6735a input=a9049054013a1b77]*/ python-frozendict/src/frozendict/c_src/3_8/cpython_src/Objects/dict-common.h000066400000000000000000000043361470250572200275040ustar00rootroot00000000000000#ifndef Py_DICT_COMMON_H #define Py_DICT_COMMON_H typedef struct { /* Cached hash code of me_key. */ Py_hash_t me_hash; PyObject *me_key; PyObject *me_value; /* This field is only meaningful for combined tables */ } PyDictKeyEntry; /* dict_lookup_func() returns index of entry which can be used like DK_ENTRIES(dk)[index]. * -1 when no entry found, -3 when compare raises error. */ typedef Py_ssize_t (*dict_lookup_func) (PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); #define DKIX_EMPTY (-1) #define DKIX_DUMMY (-2) /* Used internally */ #define DKIX_ERROR (-3) /* See dictobject.c for actual layout of DictKeysObject */ struct _dictkeysobject { Py_ssize_t dk_refcnt; /* Size of the hash table (dk_indices). It must be a power of 2. */ Py_ssize_t dk_size; /* Function to lookup in the hash table (dk_indices): - lookdict(): general-purpose, and may return DKIX_ERROR if (and only if) a comparison raises an exception. - lookdict_unicode(): specialized to Unicode string keys, comparison of which can never raise an exception; that function can never return DKIX_ERROR. - lookdict_unicode_nodummy(): similar to lookdict_unicode() but further specialized for Unicode string keys that cannot be the value. - lookdict_split(): Version of lookdict() for split tables. */ dict_lookup_func dk_lookup; /* Number of usable entries in dk_entries. */ Py_ssize_t dk_usable; /* Number of used entries in dk_entries. */ Py_ssize_t dk_nentries; /* Actual hash table of dk_size entries. It holds indices in dk_entries, or DKIX_EMPTY(-1) or DKIX_DUMMY(-2). Indices must be: 0 <= indice < USABLE_FRACTION(dk_size). The size in bytes of an indice depends on dk_size: - 1 byte if dk_size <= 0xff (char*) - 2 bytes if dk_size <= 0xffff (int16_t*) - 4 bytes if dk_size <= 0xffffffff (int32_t*) - 8 bytes otherwise (int64_t*) Dynamically sized, SIZEOF_VOID_P is minimum. */ char dk_indices[]; /* char is required to avoid strict aliasing. */ /* "PyDictKeyEntry dk_entries[dk_usable];" array follows: see the DK_ENTRIES() macro */ }; #endif python-frozendict/src/frozendict/c_src/3_8/cpython_src/Objects/dictobject.c000066400000000000000000001613161470250572200274020ustar00rootroot00000000000000/* Dictionary object implementation using a hash table */ /* The distribution includes a separate file, Objects/dictnotes.txt, describing explorations into dictionary design and optimization. It covers typical dictionary use patterns, the parameters for tuning dictionaries, and several ideas for possible optimizations. */ /* PyDictKeysObject This implements the dictionary's hashtable. As of Python 3.6, this is compact and ordered. Basic idea is described here: * https://mail.python.org/pipermail/python-dev/2012-December/123028.html * https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html layout: +---------------+ | dk_refcnt | | dk_size | | dk_lookup | | dk_usable | | dk_nentries | +---------------+ | dk_indices | | | +---------------+ | dk_entries | | | +---------------+ dk_indices is actual hashtable. It holds index in entries, or DKIX_EMPTY(-1) or DKIX_DUMMY(-2). Size of indices is dk_size. Type of each index in indices is vary on dk_size: * int8 for dk_size <= 128 * int16 for 256 <= dk_size <= 2**15 * int32 for 2**16 <= dk_size <= 2**31 * int64 for 2**32 <= dk_size dk_entries is array of PyDictKeyEntry. Its size is USABLE_FRACTION(dk_size). DK_ENTRIES(dk) can be used to get pointer to entries. NOTE: Since negative value is used for DKIX_EMPTY and DKIX_DUMMY, type of dk_indices entry is signed integer and int16 is used for table which dk_size == 256. */ /* The DictObject can be in one of two forms. Either: A combined table: ma_values == NULL, dk_refcnt == 1. Values are stored in the me_value field of the PyDictKeysObject. Or: A split table: ma_values != NULL, dk_refcnt >= 1 Values are stored in the ma_values array. Only string (unicode) keys are allowed. All dicts sharing same key must have same insertion order. There are four kinds of slots in the table (slot is index, and DK_ENTRIES(keys)[index] if index >= 0): 1. Unused. index == DKIX_EMPTY Does not hold an active (key, value) pair now and never did. Unused can transition to Active upon key insertion. This is each slot's initial state. 2. Active. index >= 0, me_key != NULL and me_value != NULL Holds an active (key, value) pair. Active can transition to Dummy or Pending upon key deletion (for combined and split tables respectively). This is the only case in which me_value != NULL. 3. Dummy. index == DKIX_DUMMY (combined only) Previously held an active (key, value) pair, but that was deleted and an active pair has not yet overwritten the slot. Dummy can transition to Active upon key insertion. Dummy slots cannot be made Unused again else the probe sequence in case of collision would have no way to know they were once active. 4. Pending. index >= 0, key != NULL, and value == NULL (split only) Not yet inserted in split-table. */ /* Preserving insertion order It's simple for combined table. Since dk_entries is mostly append only, we can get insertion order by just iterating dk_entries. One exception is .popitem(). It removes last item in dk_entries and decrement dk_nentries to achieve amortized O(1). Since there are DKIX_DUMMY remains in dk_indices, we can't increment dk_usable even though dk_nentries is decremented. In split table, inserting into pending entry is allowed only for dk_entries[ix] where ix == mp->ma_used. Inserting into other index and deleting item cause converting the dict to the combined table. */ /* PyDict_MINSIZE is the starting size for any new dict. * 8 allows dicts with no more than 5 active entries; experiments suggested * this suffices for the majority of dicts (consisting mostly of usually-small * dicts created to pass keyword arguments). * Making this 8, rather than 4 reduces the number of resizes for most * dictionaries, without any significant extra memory use. */ #define PyDict_MINSIZE 8 #include "Python.h" #include "dict-common.h" #include "stringlib/eq.h" // unicode_eq() /*[clinic input] class dict "PyDictObject *" "&PyDict_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=f157a5a0ce9589d6]*/ /* To ensure the lookup algorithm terminates, there must be at least one Unused slot (NULL key) in the table. To avoid slowing down lookups on a near-full table, we resize the table when it's USABLE_FRACTION (currently two-thirds) full. */ #define PERTURB_SHIFT 5 /* Major subtleties ahead: Most hash schemes depend on having a "good" hash function, in the sense of simulating randomness. Python doesn't: its most important hash functions (for ints) are very regular in common cases: >>>[hash(i) for i in range(4)] [0, 1, 2, 3] This isn't necessarily bad! To the contrary, in a table of size 2**i, taking the low-order i bits as the initial table index is extremely fast, and there are no collisions at all for dicts indexed by a contiguous range of ints. So this gives better-than-random behavior in common cases, and that's very desirable. OTOH, when collisions occur, the tendency to fill contiguous slices of the hash table makes a good collision resolution strategy crucial. Taking only the last i bits of the hash code is also vulnerable: for example, consider the list [i << 16 for i in range(20000)] as a set of keys. Since ints are their own hash codes, and this fits in a dict of size 2**15, the last 15 bits of every hash code are all 0: they *all* map to the same table index. But catering to unusual cases should not slow the usual ones, so we just take the last i bits anyway. It's up to collision resolution to do the rest. If we *usually* find the key we're looking for on the first try (and, it turns out, we usually do -- the table load factor is kept under 2/3, so the odds are solidly in our favor), then it makes best sense to keep the initial index computation dirt cheap. The first half of collision resolution is to visit table indices via this recurrence: j = ((5*j) + 1) mod 2**i For any initial j in range(2**i), repeating that 2**i times generates each int in range(2**i) exactly once (see any text on random-number generation for proof). By itself, this doesn't help much: like linear probing (setting j += 1, or j -= 1, on each loop trip), it scans the table entries in a fixed order. This would be bad, except that's not the only thing we do, and it's actually *good* in the common cases where hash keys are consecutive. In an example that's really too small to make this entirely clear, for a table of size 2**3 the order of indices is: 0 -> 1 -> 6 -> 7 -> 4 -> 5 -> 2 -> 3 -> 0 [and here it's repeating] If two things come in at index 5, the first place we look after is index 2, not 6, so if another comes in at index 6 the collision at 5 didn't hurt it. Linear probing is deadly in this case because there the fixed probe order is the *same* as the order consecutive keys are likely to arrive. But it's extremely unlikely hash codes will follow a 5*j+1 recurrence by accident, and certain that consecutive hash codes do not. The other half of the strategy is to get the other bits of the hash code into play. This is done by initializing a (unsigned) vrbl "perturb" to the full hash code, and changing the recurrence to: perturb >>= PERTURB_SHIFT; j = (5*j) + 1 + perturb; use j % 2**i as the next table index; Now the probe sequence depends (eventually) on every bit in the hash code, and the pseudo-scrambling property of recurring on 5*j+1 is more valuable, because it quickly magnifies small differences in the bits that didn't affect the initial index. Note that because perturb is unsigned, if the recurrence is executed often enough perturb eventually becomes and remains 0. At that point (very rarely reached) the recurrence is on (just) 5*j+1 again, and that's certain to find an empty slot eventually (since it generates every int in range(2**i), and we make sure there's always at least one empty slot). Selecting a good value for PERTURB_SHIFT is a balancing act. You want it small so that the high bits of the hash code continue to affect the probe sequence across iterations; but you want it large so that in really bad cases the high-order hash bits have an effect on early iterations. 5 was "the best" in minimizing total collisions across experiments Tim Peters ran (on both normal and pathological cases), but 4 and 6 weren't significantly worse. Historical: Reimer Behrends contributed the idea of using a polynomial-based approach, using repeated multiplication by x in GF(2**n) where an irreducible polynomial for each table size was chosen such that x was a primitive root. Christian Tismer later extended that to use division by x instead, as an efficient way to get the high bits of the hash code into play. This scheme also gave excellent collision statistics, but was more expensive: two if-tests were required inside the loop; computing "the next" index took about the same number of operations but without as much potential parallelism (e.g., computing 5*j can go on at the same time as computing 1+perturb in the above, and then shifting perturb can be done while the table index is being masked); and the PyDictObject struct required a member to hold the table's polynomial. In Tim's experiments the current scheme ran faster, produced equally good collision statistics, needed less code & used less memory. */ /* forward declarations */ static Py_ssize_t lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static Py_ssize_t lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); /*Global counter used to set ma_version_tag field of dictionary. * It is incremented each time that a dictionary is created and each * time that a dictionary is modified. */ static uint64_t pydict_global_version = 0; #define DICT_NEXT_VERSION() (++pydict_global_version) /* Dictionary reuse scheme to save calls to malloc and free */ #ifndef PyDict_MAXFREELIST #define PyDict_MAXFREELIST 80 #endif #if PyDict_MAXFREELIST > 0 static PyDictObject *free_list[PyDict_MAXFREELIST]; static int numfree = 0; static PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST]; static int numfreekeys = 0; #endif #include "clinic/dictobject.c.h" #define DK_SIZE(dk) ((dk)->dk_size) #if SIZEOF_VOID_P > 4 #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : DK_SIZE(dk) <= 0xffffffff ? \ 4 : sizeof(int64_t)) #else #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : sizeof(int32_t)) #endif #define DK_ENTRIES(dk) \ ((PyDictKeyEntry*)(&((int8_t*)((dk)->dk_indices))[DK_SIZE(dk) * DK_IXSIZE(dk)])) #define DK_MASK(dk) (((dk)->dk_size)-1) #define IS_POWER_OF_2(x) (((x) & (x-1)) == 0) static void free_keys_object(PyDictKeysObject *keys); static inline void dictkeys_incref(PyDictKeysObject *dk) { _Py_INC_REFTOTAL; dk->dk_refcnt++; } static inline void dictkeys_decref(PyDictKeysObject *dk) { assert(dk->dk_refcnt > 0); _Py_DEC_REFTOTAL; if (--dk->dk_refcnt == 0) { free_keys_object(dk); } } /* lookup indices. returns DKIX_EMPTY, DKIX_DUMMY, or ix >=0 */ static inline Py_ssize_t dictkeys_get_index(const PyDictKeysObject *keys, Py_ssize_t i) { Py_ssize_t s = DK_SIZE(keys); Py_ssize_t ix; if (s <= 0xff) { const int8_t *indices = (const int8_t*)(keys->dk_indices); ix = indices[i]; } else if (s <= 0xffff) { const int16_t *indices = (const int16_t*)(keys->dk_indices); ix = indices[i]; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { const int64_t *indices = (const int64_t*)(keys->dk_indices); ix = indices[i]; } #endif else { const int32_t *indices = (const int32_t*)(keys->dk_indices); ix = indices[i]; } assert(ix >= DKIX_DUMMY); return ix; } /* write to indices. */ static inline void dictkeys_set_index(PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix) { Py_ssize_t s = DK_SIZE(keys); assert(ix >= DKIX_DUMMY); if (s <= 0xff) { int8_t *indices = (int8_t*)(keys->dk_indices); assert(ix <= 0x7f); indices[i] = (char)ix; } else if (s <= 0xffff) { int16_t *indices = (int16_t*)(keys->dk_indices); assert(ix <= 0x7fff); indices[i] = (int16_t)ix; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { int64_t *indices = (int64_t*)(keys->dk_indices); indices[i] = ix; } #endif else { int32_t *indices = (int32_t*)(keys->dk_indices); assert(ix <= 0x7fffffff); indices[i] = (int32_t)ix; } } /* USABLE_FRACTION is the maximum dictionary load. * Increasing this ratio makes dictionaries more dense resulting in more * collisions. Decreasing it improves sparseness at the expense of spreading * indices over more cache lines and at the cost of total memory consumed. * * USABLE_FRACTION must obey the following: * (0 < USABLE_FRACTION(n) < n) for all n >= 2 * * USABLE_FRACTION should be quick to calculate. * Fractions around 1/2 to 2/3 seem to work well in practice. */ #define USABLE_FRACTION(n) (((n) << 1)/3) /* Find the smallest dk_size >= minsize. */ static inline Py_ssize_t calculate_keysize(Py_ssize_t minsize) { #if SIZEOF_LONG == SIZEOF_SIZE_T minsize = (minsize | PyDict_MINSIZE) - 1; return 1LL << _Py_bit_length(minsize | (PyDict_MINSIZE-1)); #elif defined(_MSC_VER) // On 64bit Windows, sizeof(long) == 4. minsize = (minsize | PyDict_MINSIZE) - 1; unsigned long msb; _BitScanReverse64(&msb, (uint64_t)minsize); return 1LL << (msb + 1); #else Py_ssize_t size; for (size = PyDict_MINSIZE; size < minsize && size > 0; size <<= 1) ; return size; #endif } /* estimate_keysize is reverse function of USABLE_FRACTION. * * This can be used to reserve enough size to insert n entries without * resizing. */ static inline Py_ssize_t estimate_keysize(Py_ssize_t n) { return calculate_keysize((n*3 + 1) / 2); } /* GROWTH_RATE. Growth rate upon hitting maximum load. * Currently set to used*3. * This means that dicts double in size when growing without deletions, * but have more head room when the number of deletions is on a par with the * number of insertions. See also bpo-17563 and bpo-33205. * * GROWTH_RATE was set to used*4 up to version 3.2. * GROWTH_RATE was set to used*2 in version 3.3.0 * GROWTH_RATE was set to used*2 + capacity/2 in 3.4.0-3.6.0. */ #define GROWTH_RATE(d) ((d)->ma_used*3) /* This immutable, empty PyDictKeysObject is used for PyDict_Clear() * (which cannot fail and thus can do no allocation). */ static PyDictKeysObject empty_keys_struct = { 1, /* dk_refcnt */ 1, /* dk_size */ lookdict_unicode_nodummy, /* dk_lookup */ 0, /* dk_usable (immutable) */ 0, /* dk_nentries */ {DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY}, /* dk_indices */ }; static PyObject *empty_values[1] = { NULL }; #define Py_EMPTY_KEYS &empty_keys_struct /* Uncomment to check the dict content in _PyDict_CheckConsistency() */ /* #define DEBUG_PYDICT */ #ifdef DEBUG_PYDICT # define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 1)) #else # define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 0)) #endif static int _PyDict_CheckConsistency(PyObject *op, int check_content) { #define CHECK(expr) \ do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0) assert(op != NULL); CHECK(PyAnyDict_Check(op)); PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; int splitted = _PyDict_HasSplitTable(mp); Py_ssize_t usable = USABLE_FRACTION(keys->dk_size); CHECK(0 <= mp->ma_used && mp->ma_used <= usable); CHECK(IS_POWER_OF_2(keys->dk_size)); CHECK(0 <= keys->dk_usable && keys->dk_usable <= usable); CHECK(0 <= keys->dk_nentries && keys->dk_nentries <= usable); CHECK(keys->dk_usable + keys->dk_nentries <= usable); if (!splitted) { /* combined table */ CHECK(keys->dk_refcnt == 1); } if (check_content) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i; for (i=0; i < keys->dk_size; i++) { Py_ssize_t ix = dictkeys_get_index(keys, i); CHECK(DKIX_DUMMY <= ix && ix <= usable); } for (i=0; i < usable; i++) { PyDictKeyEntry *entry = &entries[i]; PyObject *key = entry->me_key; if (key != NULL) { if (PyUnicode_CheckExact(key)) { Py_hash_t hash = ((PyASCIIObject *)key)->hash; CHECK(hash != -1); CHECK(entry->me_hash == hash); } else { /* test_dict fails if PyObject_Hash() is called again */ CHECK(entry->me_hash != -1); } if (!splitted) { CHECK(entry->me_value != NULL); } } if (splitted) { CHECK(entry->me_value == NULL); } } if (splitted) { /* splitted table */ for (i=0; i < mp->ma_used; i++) { CHECK(mp->ma_values[i] != NULL); } } } return 1; #undef CHECK } static PyDictKeysObject* new_keys_object(Py_ssize_t size) { PyDictKeysObject *dk; Py_ssize_t es, usable; assert(size >= PyDict_MINSIZE); assert(IS_POWER_OF_2(size)); usable = USABLE_FRACTION(size); if (size <= 0xff) { es = 1; } else if (size <= 0xffff) { es = 2; } #if SIZEOF_VOID_P > 4 else if (size <= 0xffffffff) { es = 4; } #endif else { es = sizeof(Py_ssize_t); } #if PyDict_MAXFREELIST > 0 if (size == PyDict_MINSIZE && numfreekeys > 0) { dk = keys_free_list[--numfreekeys]; } else #endif { dk = PyObject_Malloc(sizeof(PyDictKeysObject) + es * size + sizeof(PyDictKeyEntry) * usable); if (dk == NULL) { PyErr_NoMemory(); return NULL; } } _Py_INC_REFTOTAL; dk->dk_refcnt = 1; dk->dk_size = size; dk->dk_usable = usable; dk->dk_lookup = lookdict_unicode_nodummy; dk->dk_nentries = 0; memset(&dk->dk_indices[0], 0xff, es * size); memset(DK_ENTRIES(dk), 0, sizeof(PyDictKeyEntry) * usable); return dk; } static void free_keys_object(PyDictKeysObject *keys) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n; for (i = 0, n = keys->dk_nentries; i < n; i++) { Py_XDECREF(entries[i].me_key); Py_XDECREF(entries[i].me_value); } #if PyDict_MAXFREELIST > 0 if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) { keys_free_list[numfreekeys++] = keys; return; } #endif PyObject_Free(keys); } #define new_values(size) PyMem_NEW(PyObject *, size) #define free_values(values) PyMem_Free(values) static Py_ssize_t _d_PyDict_KeysSize(PyDictKeysObject *keys) { return (sizeof(PyDictKeysObject) + DK_IXSIZE(keys) * DK_SIZE(keys) + USABLE_FRACTION(DK_SIZE(keys)) * sizeof(PyDictKeyEntry)); } static PyDictKeysObject * clone_combined_dict_keys(PyDictObject *orig) { assert(PyAnyDict_Check(orig)); assert( Py_TYPE(orig)->tp_iter == PyDict_Type.tp_iter || Py_TYPE(orig)->tp_iter == (getiterfunc)frozendict_iter ); assert(orig->ma_values == NULL); assert(orig->ma_keys->dk_refcnt == 1); Py_ssize_t keys_size = _d_PyDict_KeysSize(orig->ma_keys); PyDictKeysObject *keys = PyObject_Malloc(keys_size); if (keys == NULL) { PyErr_NoMemory(); return NULL; } memcpy(keys, orig->ma_keys, keys_size); /* After copying key/value pairs, we need to incref all keys and values and they are about to be co-owned by a new dict object. */ PyDictKeyEntry *ep0 = DK_ENTRIES(keys); Py_ssize_t n = keys->dk_nentries; for (Py_ssize_t i = 0; i < n; i++) { PyDictKeyEntry *entry = &ep0[i]; PyObject *value = entry->me_value; Py_INCREF(value); Py_INCREF(entry->me_key); } /* Since we copied the keys table we now have an extra reference in the system. Manually call _Py_INC_REFTOTAL to signal that we have it now; calling dictkeys_incref would be an error as keys->dk_refcnt is already set to 1 (after memcpy). */ _Py_INC_REFTOTAL; return keys; } /* The basic lookup function used by all operations. This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4. Open addressing is preferred over chaining since the link overhead for chaining would be substantial (100% with typical malloc overhead). The initial probe index is computed as hash mod the table size. Subsequent probe indices are computed as explained earlier. All arithmetic on hash should ignore overflow. The details in this version are due to Tim Peters, building on many past contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and Christian Tismer. lookdict() is general-purpose, and may return DKIX_ERROR if (and only if) a comparison raises an exception. lookdict_unicode() below is specialized to string keys, comparison of which can never raise an exception; that function can never return DKIX_ERROR when key is string. Otherwise, it falls back to lookdict(). lookdict_unicode_nodummy is further specialized for string keys that cannot be the value. For both, when the key isn't found a DKIX_EMPTY is returned. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { size_t i, mask, perturb; PyDictKeysObject *dk; PyDictKeyEntry *ep0; top: dk = mp->ma_keys; ep0 = DK_ENTRIES(dk); mask = DK_MASK(dk); perturb = hash; i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(dk, i); if (ix == DKIX_EMPTY) { *value_addr = NULL; return ix; } if (ix >= 0) { PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); if (ep->me_key == key) { *value_addr = ep->me_value; return ix; } if (ep->me_hash == hash) { PyObject *startkey = ep->me_key; Py_INCREF(startkey); int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); Py_DECREF(startkey); if (cmp < 0) { *value_addr = NULL; return DKIX_ERROR; } if (dk == mp->ma_keys && ep->me_key == startkey) { if (cmp > 0) { *value_addr = ep->me_value; return ix; } } else { /* The dict was mutated, restart */ goto top; } } } perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; } Py_UNREACHABLE(); } /* Faster version of lookdict_unicode when it is known that no keys * will be present. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { assert(mp->ma_values == NULL); /* Make sure this function doesn't have to handle non-unicode keys, including subclasses of str; e.g., one reason to subclass unicodes is to override __eq__, and for speed we don't cater to that here. */ if (!PyUnicode_CheckExact(key)) { return lookdict(mp, key, hash, value_addr); } PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); size_t mask = DK_MASK(mp->ma_keys); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i); assert (ix != DKIX_DUMMY); if (ix == DKIX_EMPTY) { *value_addr = NULL; return DKIX_EMPTY; } PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { *value_addr = ep->me_value; return ix; } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } #define MAINTAIN_TRACKING(mp, key, value) \ do { \ if (!_PyObject_GC_IS_TRACKED(mp)) { \ if (_PyObject_GC_MAY_BE_TRACKED(key) || \ _PyObject_GC_MAY_BE_TRACKED(value)) { \ PyObject_GC_Track(mp); \ } \ } \ } while(0) /* Internal function to find slot for an item from its hash when it is known that the key is not present in the dict. The dict must be combined. */ static Py_ssize_t find_empty_slot(PyDictKeysObject *keys, Py_hash_t hash) { assert(keys != NULL); const size_t mask = DK_MASK(keys); size_t i = hash & mask; Py_ssize_t ix = dictkeys_get_index(keys, i); for (size_t perturb = hash; ix >= 0;) { perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; ix = dictkeys_get_index(keys, i); } return i; } /* Internal routine used by dictresize() to build a hashtable of entries. */ static void build_indices(PyDictKeysObject *keys, PyDictKeyEntry *ep, Py_ssize_t n) { size_t mask = (size_t)DK_SIZE(keys) - 1; for (Py_ssize_t ix = 0; ix != n; ix++, ep++) { Py_hash_t hash = ep->me_hash; size_t i = hash & mask; for (size_t perturb = hash; dictkeys_get_index(keys, i) != DKIX_EMPTY;) { perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } dictkeys_set_index(keys, i, ix); } } static int _d_PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue, Py_hash_t *phash) { Py_ssize_t i; PyDictObject *mp; PyDictKeyEntry *entry_ptr; PyObject *value; if (!PyAnyDict_Check(op)) return 0; mp = (PyDictObject *)op; i = *ppos; if (mp->ma_values) { if (i < 0 || i >= mp->ma_used) return 0; /* values of split table is always dense */ entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; value = mp->ma_values[i]; assert(value != NULL); } else { Py_ssize_t n = mp->ma_keys->dk_nentries; if (i < 0 || i >= n) return 0; entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) return 0; value = entry_ptr->me_value; } *ppos = i+1; if (pkey) *pkey = entry_ptr->me_key; if (phash) *phash = entry_ptr->me_hash; if (pvalue) *pvalue = value; return 1; } /* Methods */ static void dict_dealloc(PyDictObject *mp) { PyObject **values = mp->ma_values; PyDictKeysObject *keys = mp->ma_keys; Py_ssize_t i, n; /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(mp); Py_TRASHCAN_BEGIN(mp, dict_dealloc) if (values != NULL) { if (values != empty_values) { for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) { Py_XDECREF(values[i]); } free_values(values); } dictkeys_decref(keys); } else if (keys != NULL) { assert(keys->dk_refcnt == 1 || keys == Py_EMPTY_KEYS); dictkeys_decref(keys); } #if PyDict_MAXFREELIST > 0 if (numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type)) { free_list[numfree++] = mp; } else #endif { Py_TYPE(mp)->tp_free((PyObject *)mp); } Py_TRASHCAN_END } static PyObject * dict_repr(PyDictObject *mp) { Py_ssize_t i; PyObject *key = NULL, *value = NULL; _PyUnicodeWriter writer; int first; i = Py_ReprEnter((PyObject *)mp); if (i != 0) { return i > 0 ? PyUnicode_FromString("{...}") : NULL; } if (mp->ma_used == 0) { Py_ReprLeave((PyObject *)mp); return PyUnicode_FromString("{}"); } _PyUnicodeWriter_Init(&writer); writer.overallocate = 1; /* "{" + "1: 2" + ", 3: 4" * (len - 1) + "}" */ writer.min_length = 1 + 4 + (2 + 4) * (mp->ma_used - 1) + 1; if (_PyUnicodeWriter_WriteChar(&writer, '{') < 0) goto error; /* Do repr() on each key+value pair, and insert ": " between them. Note that repr may mutate the dict. */ i = 0; first = 1; while (_d_PyDict_Next((PyObject *)mp, &i, &key, &value, NULL)) { PyObject *s; int res; /* Prevent repr from deleting key or value during key format. */ Py_INCREF(key); Py_INCREF(value); if (!first) { if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0) goto error; } first = 0; s = PyObject_Repr(key); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; if (_PyUnicodeWriter_WriteASCIIString(&writer, ": ", 2) < 0) goto error; s = PyObject_Repr(value); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; Py_CLEAR(key); Py_CLEAR(value); } writer.overallocate = 0; if (_PyUnicodeWriter_WriteChar(&writer, '}') < 0) goto error; Py_ReprLeave((PyObject *)mp); return _PyUnicodeWriter_Finish(&writer); error: Py_ReprLeave((PyObject *)mp); _PyUnicodeWriter_Dealloc(&writer); Py_XDECREF(key); Py_XDECREF(value); return NULL; } static Py_ssize_t dict_length(PyDictObject *mp) { return mp->ma_used; } static PyObject * dict_subscript(PyDictObject *mp, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || value == NULL) { if (!PyAnyDict_CheckExact(mp)) { /* Look up __missing__ method if we're a subclass. */ PyObject *missing, *res; _Py_IDENTIFIER(__missing__); missing = _PyObject_LookupSpecial((PyObject *)mp, &PyId___missing__); if (missing != NULL) { res = PyObject_CallFunctionObjArgs(missing, key, NULL); Py_DECREF(missing); return res; } else if (PyErr_Occurred()) return NULL; } _PyErr_SetKeyError(key); return NULL; } Py_INCREF(value); return value; } static PyObject * dict_richcompare(PyObject *v, PyObject *w, int op) { int cmp; PyObject *res; if (!PyAnyDict_Check(v) || !PyAnyDict_Check(w)) { res = Py_NotImplemented; } else if (op == Py_EQ || op == Py_NE) { cmp = frozendict_equal((PyDictObject *)v, (PyDictObject *)w); if (cmp < 0) return NULL; res = (cmp == (op == Py_EQ)) ? Py_True : Py_False; } else res = Py_NotImplemented; Py_INCREF(res); return res; } /*[clinic input] @coexist dict.__contains__ key: object / True if the dictionary has the specified key, else False. [clinic start generated code]*/ static PyObject * dict___contains__(PyDictObject *self, PyObject *key) /*[clinic end generated code: output=a3d03db709ed6e6b input=fe1cb42ad831e820]*/ { register PyDictObject *mp = self; Py_hash_t hash; Py_ssize_t ix; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || value == NULL) Py_RETURN_FALSE; Py_RETURN_TRUE; } /*[clinic input] dict.get key: object default: object = None / Return the value for key if key is in the dictionary, else default. [clinic start generated code]*/ static PyObject * dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value) /*[clinic end generated code: output=bba707729dee05bf input=279ddb5790b6b107]*/ { PyObject *val = NULL; Py_hash_t hash; Py_ssize_t ix; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (self->ma_keys->dk_lookup) (self, key, hash, &val); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || val == NULL) { val = default_value; } Py_INCREF(val); return val; } static int dict_traverse(PyObject *op, visitproc visit, void *arg) { PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n = keys->dk_nentries; if (keys->dk_lookup == lookdict) { for (i = 0; i < n; i++) { if (entries[i].me_value != NULL) { Py_VISIT(entries[i].me_value); Py_VISIT(entries[i].me_key); } } } else { if (mp->ma_values != NULL) { for (i = 0; i < n; i++) { Py_VISIT(mp->ma_values[i]); } } else { for (i = 0; i < n; i++) { Py_VISIT(entries[i].me_value); } } } return 0; } static PyObject *dictiter_new(PyDictObject *, PyTypeObject *); static PyObject * dict_sizeof(PyDictObject *mp, PyObject *Py_UNUSED(ignored)) { return PyLong_FromSsize_t(_PyDict_SizeOf(mp)); } PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]"); PyDoc_STRVAR(sizeof__doc__, "D.__sizeof__() -> size of D in memory, in bytes"); PyDoc_STRVAR(copy__doc__, "D.copy() -> a shallow copy of D"); PyDoc_STRVAR(keys__doc__, "D.keys() -> a set-like object providing a view on D's keys"); PyDoc_STRVAR(items__doc__, "D.items() -> a set-like object providing a view on D's items"); PyDoc_STRVAR(values__doc__, "D.values() -> an object providing a view on D's values"); /* Hack to implement "key in dict" */ static PySequenceMethods dict_as_sequence = { 0, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ PyDict_Contains, /* sq_contains */ 0, /* sq_inplace_concat */ 0, /* sq_inplace_repeat */ }; /* Dictionary iterator types */ typedef struct { PyObject_HEAD PyDictObject *di_dict; /* Set to NULL when iterator is exhausted */ Py_ssize_t di_used; Py_ssize_t di_pos; PyObject* di_result; /* reusable result tuple for iteritems */ Py_ssize_t len; } dictiterobject; static PyObject * dictiter_new(PyDictObject *dict, PyTypeObject *itertype) { dictiterobject *di; di = PyObject_GC_New(dictiterobject, itertype); if (di == NULL) { return NULL; } Py_INCREF(dict); di->di_dict = dict; di->di_used = dict->ma_used; di->len = dict->ma_used; if (itertype == &PyDictRevIterKey_Type || itertype == &PyDictRevIterItem_Type || itertype == &PyDictRevIterValue_Type) { if (dict->ma_values) { di->di_pos = dict->ma_used - 1; } else { di->di_pos = dict->ma_keys->dk_nentries - 1; } } else { di->di_pos = 0; } if ( itertype == &PyFrozenDictIterItem_Type || itertype == &PyDictRevIterItem_Type ) { di->di_result = PyTuple_Pack(2, Py_None, Py_None); if (di->di_result == NULL) { Py_DECREF(di); return NULL; } } else { di->di_result = NULL; } PyObject_GC_Track(di); return (PyObject *)di; } static void dictiter_dealloc(dictiterobject *di) { /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(di); Py_XDECREF(di->di_dict); Py_XDECREF(di->di_result); PyObject_GC_Del(di); } static int dictiter_traverse(dictiterobject *di, visitproc visit, void *arg) { Py_VISIT(di->di_dict); Py_VISIT(di->di_result); return 0; } static PyObject * dictiter_len(dictiterobject *di, PyObject *Py_UNUSED(ignored)) { Py_ssize_t len = 0; if (di->di_dict != NULL && di->di_used == di->di_dict->ma_used) len = di->len; return PyLong_FromSize_t(len); } PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); static PyObject * dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyMethodDef dictiter_methods[] = { {"__length_hint__", (PyCFunction)(void(*)(void))dictiter_len, METH_NOARGS, length_hint_doc}, {"__reduce__", (PyCFunction)(void(*)(void))dictiter_reduce, METH_NOARGS, reduce_doc}, {NULL, NULL} /* sentinel */ }; /* dictreviter */ /*[clinic input] dict.__reversed__ Return a reverse iterator over the dict keys. [clinic start generated code]*/ static PyObject * dict___reversed___impl(PyDictObject *self) /*[clinic end generated code: output=e674483336d1ed51 input=23210ef3477d8c4d]*/ { assert (PyAnyDict_Check(self)); return dictiter_new(self, &PyDictRevIterKey_Type); } static PyObject * dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)) { _Py_IDENTIFIER(iter); /* copy the iterator state */ dictiterobject tmp = *di; Py_XINCREF(tmp.di_dict); PyObject *list = PySequence_List((PyObject*)&tmp); Py_XDECREF(tmp.di_dict); if (list == NULL) { return NULL; } return Py_BuildValue("N(N)", _PyEval_GetBuiltinId(&PyId_iter), list); } /***********************************************/ /* View objects for keys(), items(), values(). */ /***********************************************/ /* The instance lay-out is the same for all three; but the type differs. */ static void dictview_dealloc(_PyDictViewObject *dv) { /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(dv); Py_XDECREF(dv->dv_dict); PyObject_GC_Del(dv); } static int dictview_traverse(_PyDictViewObject *dv, visitproc visit, void *arg) { Py_VISIT(dv->dv_dict); return 0; } static Py_ssize_t dictview_len(_PyDictViewObject *dv) { Py_ssize_t len = 0; if (dv->dv_dict != NULL) len = dv->dv_dict->ma_used; return len; } static PyObject * _d_PyDictView_New(PyObject *dict, PyTypeObject *type) { _PyDictViewObject *dv; if (dict == NULL) { PyErr_BadInternalCall(); return NULL; } if (!PyAnyDict_Check(dict)) { /* XXX Get rid of this restriction later */ PyErr_Format(PyExc_TypeError, "%s() requires a dict argument, not '%s'", type->tp_name, Py_TYPE(dict)->tp_name); return NULL; } dv = PyObject_GC_New(_PyDictViewObject, type); if (dv == NULL) return NULL; Py_INCREF(dict); dv->dv_dict = (PyDictObject *)dict; PyObject_GC_Track(dv); return (PyObject *)dv; } static PyObject * dictview_mapping(PyObject *view, void *Py_UNUSED(ignored)) { assert(view != NULL); assert(PyAnyDictKeys_Check(view) || PyAnyDictValues_Check(view) || PyAnyDictItems_Check(view)); PyObject *mapping = (PyObject *)((_PyDictViewObject *)view)->dv_dict; return PyDictProxy_New(mapping); } static PyGetSetDef dictview_getset[] = { {"mapping", dictview_mapping, (setter)NULL, "dictionary that this view refers to", NULL}, {0} }; /* TODO(guido): The views objects are not complete: * support more set operations * support arbitrary mappings? - either these should be static or exported in dictobject.h - if public then they should probably be in builtins */ /* Return 1 if self is a subset of other, iterating over self; 0 if not; -1 if an error occurred. */ static int all_contained_in(PyObject *self, PyObject *other) { PyObject *iter = PyObject_GetIter(self); int ok = 1; if (iter == NULL) return -1; for (;;) { PyObject *next = PyIter_Next(iter); if (next == NULL) { if (PyErr_Occurred()) ok = -1; break; } ok = PySequence_Contains(other, next); Py_DECREF(next); if (ok <= 0) break; } Py_DECREF(iter); return ok; } static PyObject * dictview_richcompare(PyObject *self, PyObject *other, int op) { Py_ssize_t len_self, len_other; int ok; PyObject *result; assert(self != NULL); assert(PyAnyDictViewSet_Check(self)); assert(other != NULL); if (!PyAnySet_Check(other) && !PyAnyDictViewSet_Check(other)) Py_RETURN_NOTIMPLEMENTED; len_self = PyObject_Size(self); if (len_self < 0) return NULL; len_other = PyObject_Size(other); if (len_other < 0) return NULL; ok = 0; switch(op) { case Py_NE: case Py_EQ: if (len_self == len_other) ok = all_contained_in(self, other); if (op == Py_NE && ok >= 0) ok = !ok; break; case Py_LT: if (len_self < len_other) ok = all_contained_in(self, other); break; case Py_LE: if (len_self <= len_other) ok = all_contained_in(self, other); break; case Py_GT: if (len_self > len_other) ok = all_contained_in(other, self); break; case Py_GE: if (len_self >= len_other) ok = all_contained_in(other, self); break; } if (ok < 0) return NULL; result = ok ? Py_True : Py_False; Py_INCREF(result); return result; } static PyObject * dictview_repr(_PyDictViewObject *dv) { PyObject *seq; PyObject *result = NULL; Py_ssize_t rc; rc = Py_ReprEnter((PyObject *)dv); if (rc != 0) { return rc > 0 ? PyUnicode_FromString("...") : NULL; } seq = PySequence_List((PyObject *)dv); if (seq == NULL) { goto Done; } result = PyUnicode_FromFormat("%s(%R)", Py_TYPE(dv)->tp_name, seq); Py_DECREF(seq); Done: Py_ReprLeave((PyObject *)dv); return result; } /*** dict_keys ***/ static int dictkeys_contains(_PyDictViewObject *dv, PyObject *obj) { if (dv->dv_dict == NULL) return 0; return PyDict_Contains((PyObject *)dv->dv_dict, obj); } static PySequenceMethods dictkeys_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictkeys_contains, /* sq_contains */ }; // Create an set object from dictviews object. // Returns a new reference. // This utility function is used by set operations. static PyObject* dictviews_to_set(PyObject *self) { PyObject *left = self; if (PyAnyDictKeys_Check(self)) { // PySet_New() has fast path for the dict object. PyObject *dict = (PyObject *)((_PyDictViewObject *)self)->dv_dict; if (PyAnyDict_CheckExact(dict)) { left = dict; } } return PySet_New(left); } static PyObject* dictviews_sub(PyObject* self, PyObject *other) { PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } _Py_IDENTIFIER(difference_update); PyObject *tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_difference_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static int dictitems_contains(_PyDictViewObject *dv, PyObject *obj); static PyObject* _d_PyDictView_Intersect(PyObject* self, PyObject *other) { PyObject *result; PyObject *it; PyObject *key; Py_ssize_t len_self; int rv; int (*dict_contains)(_PyDictViewObject *, PyObject *); /* Python interpreter swaps parameters when dict view is on right side of & */ if (!PyAnyDictViewSet_Check(self)) { PyObject *tmp = other; other = self; self = tmp; } len_self = dictview_len((_PyDictViewObject *)self); /* if other is a set and self is smaller than other, reuse set intersection logic */ if (PySet_CheckExact(other) && len_self <= PyObject_Size(other)) { _Py_IDENTIFIER(intersection); return _PyObject_CallMethodIdObjArgs(other, &PyId_intersection, self, NULL); } /* if other is another dict view, and it is bigger than self, swap them */ if (PyAnyDictViewSet_Check(other)) { Py_ssize_t len_other = dictview_len((_PyDictViewObject *)other); if (len_other > len_self) { PyObject *tmp = other; other = self; self = tmp; } } /* at this point, two things should be true 1. self is a dictview 2. if other is a dictview then it is smaller than self */ result = PySet_New(NULL); if (result == NULL) return NULL; it = PyObject_GetIter(other); if (it == NULL) { Py_DECREF(result); return NULL; } if (PyAnyDictKeys_Check(self)) { dict_contains = dictkeys_contains; } /* else PyDictItems_Check(self) */ else { dict_contains = dictitems_contains; } while ((key = PyIter_Next(it)) != NULL) { rv = dict_contains((_PyDictViewObject *)self, key); if (rv < 0) { goto error; } if (rv) { if (PySet_Add(result, key)) { goto error; } } Py_DECREF(key); } Py_DECREF(it); if (PyErr_Occurred()) { Py_DECREF(result); return NULL; } return result; error: Py_DECREF(it); Py_DECREF(result); Py_DECREF(key); return NULL; } static PyObject* dictviews_or(PyObject* self, PyObject *other) { PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } if (_PySet_Update(result, other) < 0) { Py_DECREF(result); return NULL; } return result; } static PyObject * dictitems_xor(PyObject *self, PyObject *other) { assert(PyAnyDictItems_Check(self)); assert(PyAnyDictItems_Check(other)); PyObject *d1 = (PyObject *)((_PyDictViewObject *)self)->dv_dict; PyObject *d2 = (PyObject *)((_PyDictViewObject *)other)->dv_dict; PyObject *temp_dict; if (PyDict_CheckExact(d1)) { temp_dict = PyDict_Copy(d1); } else { PyObject* args = PyTuple_New(1); if (args == NULL) { return NULL; } Py_INCREF(d1); PyTuple_SET_ITEM(args, 0, d1); temp_dict = PyObject_Call((PyObject *) &PyDict_Type, args, NULL); Py_DECREF(args); } if (temp_dict == NULL) { return NULL; } PyObject *result_set = PySet_New(NULL); if (result_set == NULL) { Py_CLEAR(temp_dict); return NULL; } PyObject *key = NULL, *val1 = NULL, *val2 = NULL; Py_ssize_t pos = 0; Py_hash_t hash; while (_d_PyDict_Next(d2, &pos, &key, &val2, &hash)) { Py_INCREF(key); Py_INCREF(val2); val1 = _PyDict_GetItem_KnownHash(temp_dict, key, hash); int to_delete; if (val1 == NULL) { if (PyErr_Occurred()) { goto error; } to_delete = 0; } else { Py_INCREF(val1); to_delete = PyObject_RichCompareBool(val1, val2, Py_EQ); if (to_delete < 0) { goto error; } } if (to_delete) { if (_PyDict_DelItem_KnownHash(temp_dict, key, hash) < 0) { goto error; } } else { PyObject *pair = PyTuple_Pack(2, key, val2); if (pair == NULL) { goto error; } if (PySet_Add(result_set, pair) < 0) { Py_DECREF(pair); goto error; } Py_DECREF(pair); } Py_DECREF(key); Py_XDECREF(val1); Py_DECREF(val2); } key = val1 = val2 = NULL; _Py_IDENTIFIER(items); PyObject *remaining_pairs = _PyObject_CallMethodIdObjArgs(temp_dict, &PyId_items, NULL); if (remaining_pairs == NULL) { goto error; } if (_PySet_Update(result_set, remaining_pairs) < 0) { Py_DECREF(remaining_pairs); goto error; } Py_DECREF(temp_dict); Py_DECREF(remaining_pairs); return result_set; error: Py_XDECREF(temp_dict); Py_XDECREF(result_set); Py_XDECREF(key); Py_XDECREF(val1); Py_XDECREF(val2); return NULL; } static PyObject* dictviews_xor(PyObject* self, PyObject *other) { if (PyAnyDictItems_Check(self) && PyAnyDictItems_Check(other)) { return dictitems_xor(self, other); } PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } _Py_IDENTIFIER(symmetric_difference_update); PyObject *tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_symmetric_difference_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static PyNumberMethods dictviews_as_number = { 0, /*nb_add*/ (binaryfunc)dictviews_sub, /*nb_subtract*/ 0, /*nb_multiply*/ 0, /*nb_remainder*/ 0, /*nb_divmod*/ 0, /*nb_power*/ 0, /*nb_negative*/ 0, /*nb_positive*/ 0, /*nb_absolute*/ 0, /*nb_bool*/ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ (binaryfunc)_d_PyDictView_Intersect,/*nb_and*/ (binaryfunc)dictviews_xor, /*nb_xor*/ (binaryfunc)dictviews_or, /*nb_or*/ }; static PyObject* dictviews_isdisjoint(PyObject *self, PyObject *other) { PyObject *it; PyObject *item = NULL; if (self == other) { if (dictview_len((_PyDictViewObject *)self) == 0) Py_RETURN_TRUE; else Py_RETURN_FALSE; } /* Iterate over the shorter object (only if other is a set, * because PySequence_Contains may be expensive otherwise): */ if (PyAnySet_Check(other) || PyAnyDictViewSet_Check(other)) { Py_ssize_t len_self = dictview_len((_PyDictViewObject *)self); Py_ssize_t len_other = PyObject_Size(other); if (len_other == -1) return NULL; if ((len_other > len_self)) { PyObject *tmp = other; other = self; self = tmp; } } it = PyObject_GetIter(other); if (it == NULL) return NULL; while ((item = PyIter_Next(it)) != NULL) { int contains = PySequence_Contains(self, item); Py_DECREF(item); if (contains == -1) { Py_DECREF(it); return NULL; } if (contains) { Py_DECREF(it); Py_RETURN_FALSE; } } Py_DECREF(it); if (PyErr_Occurred()) return NULL; /* PyIter_Next raised an exception. */ Py_RETURN_TRUE; } PyDoc_STRVAR(isdisjoint_doc, "Return True if the view and the given iterable have a null intersection."); static PyObject* dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_keys_doc, "Return a reverse iterator over the dict keys."); static PyMethodDef dictkeys_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {"__reversed__", (PyCFunction)(void(*)(void))dictkeys_reversed, METH_NOARGS, reversed_keys_doc}, {NULL, NULL} /* sentinel */ }; static PyObject * dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterKey_Type); } /*** dict_items ***/ /* Variant of PyDict_GetItem() that doesn't suppress exceptions. This returns NULL *with* an exception set if an exception occurred. It returns NULL *without* an exception set if the key wasn't present. */ static PyObject * d_PyDict_GetItemWithError(PyObject *op, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyDictObject*mp = (PyDictObject *)op; PyObject *value; if (!PyAnyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { return NULL; } } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix < 0) return NULL; return value; } static int dictitems_contains(_PyDictViewObject *dv, PyObject *obj) { int result; PyObject *key, *value, *found; if (dv->dv_dict == NULL) return 0; if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) return 0; key = PyTuple_GET_ITEM(obj, 0); value = PyTuple_GET_ITEM(obj, 1); found = d_PyDict_GetItemWithError((PyObject *)dv->dv_dict, key); if (found == NULL) { if (PyErr_Occurred()) return -1; return 0; } Py_INCREF(found); result = PyObject_RichCompareBool(found, value, Py_EQ); Py_DECREF(found); return result; } static PySequenceMethods dictitems_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictitems_contains, /* sq_contains */ }; static PyObject* dictitems_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_items_doc, "Return a reverse iterator over the dict items."); static PyMethodDef dictitems_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {"__reversed__", (PyCFunction)dictitems_reversed, METH_NOARGS, reversed_items_doc}, {NULL, NULL} /* sentinel */ }; static PyObject * dictitems_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterItem_Type); } /*** dict_values ***/ static PySequenceMethods dictvalues_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)0, /* sq_contains */ }; static PyObject* dictvalues_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_values_doc, "Return a reverse iterator over the dict values."); static PyMethodDef dictvalues_methods[] = { {"__reversed__", (PyCFunction)dictvalues_reversed, METH_NOARGS, reversed_values_doc}, {NULL, NULL} /* sentinel */ }; static PyObject * dictvalues_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterValue_Type); } python-frozendict/src/frozendict/c_src/3_8/cpython_src/Objects/dictobject_original.c000066400000000000000000004240761470250572200312730ustar00rootroot00000000000000/* Dictionary object implementation using a hash table */ /* The distribution includes a separate file, Objects/dictnotes.txt, describing explorations into dictionary design and optimization. It covers typical dictionary use patterns, the parameters for tuning dictionaries, and several ideas for possible optimizations. */ /* PyDictKeysObject This implements the dictionary's hashtable. As of Python 3.6, this is compact and ordered. Basic idea is described here: * https://mail.python.org/pipermail/python-dev/2012-December/123028.html * https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html layout: +---------------+ | dk_refcnt | | dk_size | | dk_lookup | | dk_usable | | dk_nentries | +---------------+ | dk_indices | | | +---------------+ | dk_entries | | | +---------------+ dk_indices is actual hashtable. It holds index in entries, or DKIX_EMPTY(-1) or DKIX_DUMMY(-2). Size of indices is dk_size. Type of each index in indices is vary on dk_size: * int8 for dk_size <= 128 * int16 for 256 <= dk_size <= 2**15 * int32 for 2**16 <= dk_size <= 2**31 * int64 for 2**32 <= dk_size dk_entries is array of PyDictKeyEntry. Its size is USABLE_FRACTION(dk_size). DK_ENTRIES(dk) can be used to get pointer to entries. NOTE: Since negative value is used for DKIX_EMPTY and DKIX_DUMMY, type of dk_indices entry is signed integer and int16 is used for table which dk_size == 256. */ /* The DictObject can be in one of two forms. Either: A combined table: ma_values == NULL, dk_refcnt == 1. Values are stored in the me_value field of the PyDictKeysObject. Or: A split table: ma_values != NULL, dk_refcnt >= 1 Values are stored in the ma_values array. Only string (unicode) keys are allowed. All dicts sharing same key must have same insertion order. There are four kinds of slots in the table (slot is index, and DK_ENTRIES(keys)[index] if index >= 0): 1. Unused. index == DKIX_EMPTY Does not hold an active (key, value) pair now and never did. Unused can transition to Active upon key insertion. This is each slot's initial state. 2. Active. index >= 0, me_key != NULL and me_value != NULL Holds an active (key, value) pair. Active can transition to Dummy or Pending upon key deletion (for combined and split tables respectively). This is the only case in which me_value != NULL. 3. Dummy. index == DKIX_DUMMY (combined only) Previously held an active (key, value) pair, but that was deleted and an active pair has not yet overwritten the slot. Dummy can transition to Active upon key insertion. Dummy slots cannot be made Unused again else the probe sequence in case of collision would have no way to know they were once active. 4. Pending. index >= 0, key != NULL, and value == NULL (split only) Not yet inserted in split-table. */ /* Preserving insertion order It's simple for combined table. Since dk_entries is mostly append only, we can get insertion order by just iterating dk_entries. One exception is .popitem(). It removes last item in dk_entries and decrement dk_nentries to achieve amortized O(1). Since there are DKIX_DUMMY remains in dk_indices, we can't increment dk_usable even though dk_nentries is decremented. In split table, inserting into pending entry is allowed only for dk_entries[ix] where ix == mp->ma_used. Inserting into other index and deleting item cause converting the dict to the combined table. */ /* PyDict_MINSIZE is the starting size for any new dict. * 8 allows dicts with no more than 5 active entries; experiments suggested * this suffices for the majority of dicts (consisting mostly of usually-small * dicts created to pass keyword arguments). * Making this 8, rather than 4 reduces the number of resizes for most * dictionaries, without any significant extra memory use. */ #define PyDict_MINSIZE 8 #include "Python.h" #include "pycore_object.h" #include "pycore_pystate.h" #include "dict-common.h" #include "stringlib/eq.h" /* to get unicode_eq() */ /*[clinic input] class dict "PyDictObject *" "&PyDict_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=f157a5a0ce9589d6]*/ /* To ensure the lookup algorithm terminates, there must be at least one Unused slot (NULL key) in the table. To avoid slowing down lookups on a near-full table, we resize the table when it's USABLE_FRACTION (currently two-thirds) full. */ #define PERTURB_SHIFT 5 /* Major subtleties ahead: Most hash schemes depend on having a "good" hash function, in the sense of simulating randomness. Python doesn't: its most important hash functions (for ints) are very regular in common cases: >>>[hash(i) for i in range(4)] [0, 1, 2, 3] This isn't necessarily bad! To the contrary, in a table of size 2**i, taking the low-order i bits as the initial table index is extremely fast, and there are no collisions at all for dicts indexed by a contiguous range of ints. So this gives better-than-random behavior in common cases, and that's very desirable. OTOH, when collisions occur, the tendency to fill contiguous slices of the hash table makes a good collision resolution strategy crucial. Taking only the last i bits of the hash code is also vulnerable: for example, consider the list [i << 16 for i in range(20000)] as a set of keys. Since ints are their own hash codes, and this fits in a dict of size 2**15, the last 15 bits of every hash code are all 0: they *all* map to the same table index. But catering to unusual cases should not slow the usual ones, so we just take the last i bits anyway. It's up to collision resolution to do the rest. If we *usually* find the key we're looking for on the first try (and, it turns out, we usually do -- the table load factor is kept under 2/3, so the odds are solidly in our favor), then it makes best sense to keep the initial index computation dirt cheap. The first half of collision resolution is to visit table indices via this recurrence: j = ((5*j) + 1) mod 2**i For any initial j in range(2**i), repeating that 2**i times generates each int in range(2**i) exactly once (see any text on random-number generation for proof). By itself, this doesn't help much: like linear probing (setting j += 1, or j -= 1, on each loop trip), it scans the table entries in a fixed order. This would be bad, except that's not the only thing we do, and it's actually *good* in the common cases where hash keys are consecutive. In an example that's really too small to make this entirely clear, for a table of size 2**3 the order of indices is: 0 -> 1 -> 6 -> 7 -> 4 -> 5 -> 2 -> 3 -> 0 [and here it's repeating] If two things come in at index 5, the first place we look after is index 2, not 6, so if another comes in at index 6 the collision at 5 didn't hurt it. Linear probing is deadly in this case because there the fixed probe order is the *same* as the order consecutive keys are likely to arrive. But it's extremely unlikely hash codes will follow a 5*j+1 recurrence by accident, and certain that consecutive hash codes do not. The other half of the strategy is to get the other bits of the hash code into play. This is done by initializing a (unsigned) vrbl "perturb" to the full hash code, and changing the recurrence to: perturb >>= PERTURB_SHIFT; j = (5*j) + 1 + perturb; use j % 2**i as the next table index; Now the probe sequence depends (eventually) on every bit in the hash code, and the pseudo-scrambling property of recurring on 5*j+1 is more valuable, because it quickly magnifies small differences in the bits that didn't affect the initial index. Note that because perturb is unsigned, if the recurrence is executed often enough perturb eventually becomes and remains 0. At that point (very rarely reached) the recurrence is on (just) 5*j+1 again, and that's certain to find an empty slot eventually (since it generates every int in range(2**i), and we make sure there's always at least one empty slot). Selecting a good value for PERTURB_SHIFT is a balancing act. You want it small so that the high bits of the hash code continue to affect the probe sequence across iterations; but you want it large so that in really bad cases the high-order hash bits have an effect on early iterations. 5 was "the best" in minimizing total collisions across experiments Tim Peters ran (on both normal and pathological cases), but 4 and 6 weren't significantly worse. Historical: Reimer Behrends contributed the idea of using a polynomial-based approach, using repeated multiplication by x in GF(2**n) where an irreducible polynomial for each table size was chosen such that x was a primitive root. Christian Tismer later extended that to use division by x instead, as an efficient way to get the high bits of the hash code into play. This scheme also gave excellent collision statistics, but was more expensive: two if-tests were required inside the loop; computing "the next" index took about the same number of operations but without as much potential parallelism (e.g., computing 5*j can go on at the same time as computing 1+perturb in the above, and then shifting perturb can be done while the table index is being masked); and the PyDictObject struct required a member to hold the table's polynomial. In Tim's experiments the current scheme ran faster, produced equally good collision statistics, needed less code & used less memory. */ /* forward declarations */ static Py_ssize_t lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static Py_ssize_t lookdict_unicode(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static Py_ssize_t lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static Py_ssize_t lookdict_split(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static int dictresize(PyDictObject *mp, Py_ssize_t minused); static PyObject* dict_iter(PyDictObject *dict); /*Global counter used to set ma_version_tag field of dictionary. * It is incremented each time that a dictionary is created and each * time that a dictionary is modified. */ static uint64_t pydict_global_version = 0; #define DICT_NEXT_VERSION() (++pydict_global_version) /* Dictionary reuse scheme to save calls to malloc and free */ #ifndef PyDict_MAXFREELIST #define PyDict_MAXFREELIST 80 #endif static PyDictObject *free_list[PyDict_MAXFREELIST]; static int numfree = 0; static PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST]; static int numfreekeys = 0; #include "clinic/dictobject.c.h" int PyDict_ClearFreeList(void) { PyDictObject *op; int ret = numfree + numfreekeys; while (numfree) { op = free_list[--numfree]; assert(PyDict_CheckExact(op)); PyObject_GC_Del(op); } while (numfreekeys) { PyObject_FREE(keys_free_list[--numfreekeys]); } return ret; } /* Print summary info about the state of the optimized allocator */ void _PyDict_DebugMallocStats(FILE *out) { _PyDebugAllocatorStats(out, "free PyDictObject", numfree, sizeof(PyDictObject)); } void PyDict_Fini(void) { PyDict_ClearFreeList(); } #define DK_SIZE(dk) ((dk)->dk_size) #if SIZEOF_VOID_P > 4 #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : DK_SIZE(dk) <= 0xffffffff ? \ 4 : sizeof(int64_t)) #else #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : sizeof(int32_t)) #endif #define DK_ENTRIES(dk) \ ((PyDictKeyEntry*)(&((int8_t*)((dk)->dk_indices))[DK_SIZE(dk) * DK_IXSIZE(dk)])) #define DK_MASK(dk) (((dk)->dk_size)-1) #define IS_POWER_OF_2(x) (((x) & (x-1)) == 0) static void free_keys_object(PyDictKeysObject *keys); static inline void dictkeys_incref(PyDictKeysObject *dk) { _Py_INC_REFTOTAL; dk->dk_refcnt++; } static inline void dictkeys_decref(PyDictKeysObject *dk) { assert(dk->dk_refcnt > 0); _Py_DEC_REFTOTAL; if (--dk->dk_refcnt == 0) { free_keys_object(dk); } } /* lookup indices. returns DKIX_EMPTY, DKIX_DUMMY, or ix >=0 */ static inline Py_ssize_t dictkeys_get_index(PyDictKeysObject *keys, Py_ssize_t i) { Py_ssize_t s = DK_SIZE(keys); Py_ssize_t ix; if (s <= 0xff) { int8_t *indices = (int8_t*)(keys->dk_indices); ix = indices[i]; } else if (s <= 0xffff) { int16_t *indices = (int16_t*)(keys->dk_indices); ix = indices[i]; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { int64_t *indices = (int64_t*)(keys->dk_indices); ix = indices[i]; } #endif else { int32_t *indices = (int32_t*)(keys->dk_indices); ix = indices[i]; } assert(ix >= DKIX_DUMMY); return ix; } /* write to indices. */ static inline void dictkeys_set_index(PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix) { Py_ssize_t s = DK_SIZE(keys); assert(ix >= DKIX_DUMMY); if (s <= 0xff) { int8_t *indices = (int8_t*)(keys->dk_indices); assert(ix <= 0x7f); indices[i] = (char)ix; } else if (s <= 0xffff) { int16_t *indices = (int16_t*)(keys->dk_indices); assert(ix <= 0x7fff); indices[i] = (int16_t)ix; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { int64_t *indices = (int64_t*)(keys->dk_indices); indices[i] = ix; } #endif else { int32_t *indices = (int32_t*)(keys->dk_indices); assert(ix <= 0x7fffffff); indices[i] = (int32_t)ix; } } /* USABLE_FRACTION is the maximum dictionary load. * Increasing this ratio makes dictionaries more dense resulting in more * collisions. Decreasing it improves sparseness at the expense of spreading * indices over more cache lines and at the cost of total memory consumed. * * USABLE_FRACTION must obey the following: * (0 < USABLE_FRACTION(n) < n) for all n >= 2 * * USABLE_FRACTION should be quick to calculate. * Fractions around 1/2 to 2/3 seem to work well in practice. */ #define USABLE_FRACTION(n) (((n) << 1)/3) /* ESTIMATE_SIZE is reverse function of USABLE_FRACTION. * This can be used to reserve enough size to insert n entries without * resizing. */ #define ESTIMATE_SIZE(n) (((n)*3+1) >> 1) /* Alternative fraction that is otherwise close enough to 2n/3 to make * little difference. 8 * 2/3 == 8 * 5/8 == 5. 16 * 2/3 == 16 * 5/8 == 10. * 32 * 2/3 = 21, 32 * 5/8 = 20. * Its advantage is that it is faster to compute on machines with slow division. * #define USABLE_FRACTION(n) (((n) >> 1) + ((n) >> 2) - ((n) >> 3)) */ /* GROWTH_RATE. Growth rate upon hitting maximum load. * Currently set to used*3. * This means that dicts double in size when growing without deletions, * but have more head room when the number of deletions is on a par with the * number of insertions. See also bpo-17563 and bpo-33205. * * GROWTH_RATE was set to used*4 up to version 3.2. * GROWTH_RATE was set to used*2 in version 3.3.0 * GROWTH_RATE was set to used*2 + capacity/2 in 3.4.0-3.6.0. */ #define GROWTH_RATE(d) ((d)->ma_used*3) #define ENSURE_ALLOWS_DELETIONS(d) \ if ((d)->ma_keys->dk_lookup == lookdict_unicode_nodummy) { \ (d)->ma_keys->dk_lookup = lookdict_unicode; \ } /* This immutable, empty PyDictKeysObject is used for PyDict_Clear() * (which cannot fail and thus can do no allocation). */ static PyDictKeysObject empty_keys_struct = { 1, /* dk_refcnt */ 1, /* dk_size */ lookdict_split, /* dk_lookup */ 0, /* dk_usable (immutable) */ 0, /* dk_nentries */ {DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY}, /* dk_indices */ }; static PyObject *empty_values[1] = { NULL }; #define Py_EMPTY_KEYS &empty_keys_struct /* Uncomment to check the dict content in _PyDict_CheckConsistency() */ /* #define DEBUG_PYDICT */ #ifdef DEBUG_PYDICT # define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 1)) #else # define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 0)) #endif int _PyDict_CheckConsistency(PyObject *op, int check_content) { #define CHECK(expr) \ do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0) assert(op != NULL); CHECK(PyDict_Check(op)); PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; int splitted = _PyDict_HasSplitTable(mp); Py_ssize_t usable = USABLE_FRACTION(keys->dk_size); CHECK(0 <= mp->ma_used && mp->ma_used <= usable); CHECK(IS_POWER_OF_2(keys->dk_size)); CHECK(0 <= keys->dk_usable && keys->dk_usable <= usable); CHECK(0 <= keys->dk_nentries && keys->dk_nentries <= usable); CHECK(keys->dk_usable + keys->dk_nentries <= usable); if (!splitted) { /* combined table */ CHECK(keys->dk_refcnt == 1); } if (check_content) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i; for (i=0; i < keys->dk_size; i++) { Py_ssize_t ix = dictkeys_get_index(keys, i); CHECK(DKIX_DUMMY <= ix && ix <= usable); } for (i=0; i < usable; i++) { PyDictKeyEntry *entry = &entries[i]; PyObject *key = entry->me_key; if (key != NULL) { if (PyUnicode_CheckExact(key)) { Py_hash_t hash = ((PyASCIIObject *)key)->hash; CHECK(hash != -1); CHECK(entry->me_hash == hash); } else { /* test_dict fails if PyObject_Hash() is called again */ CHECK(entry->me_hash != -1); } if (!splitted) { CHECK(entry->me_value != NULL); } } if (splitted) { CHECK(entry->me_value == NULL); } } if (splitted) { /* splitted table */ for (i=0; i < mp->ma_used; i++) { CHECK(mp->ma_values[i] != NULL); } } } return 1; #undef CHECK } static PyDictKeysObject *new_keys_object(Py_ssize_t size) { PyDictKeysObject *dk; Py_ssize_t es, usable; assert(size >= PyDict_MINSIZE); assert(IS_POWER_OF_2(size)); usable = USABLE_FRACTION(size); if (size <= 0xff) { es = 1; } else if (size <= 0xffff) { es = 2; } #if SIZEOF_VOID_P > 4 else if (size <= 0xffffffff) { es = 4; } #endif else { es = sizeof(Py_ssize_t); } if (size == PyDict_MINSIZE && numfreekeys > 0) { dk = keys_free_list[--numfreekeys]; } else { dk = PyObject_MALLOC(sizeof(PyDictKeysObject) + es * size + sizeof(PyDictKeyEntry) * usable); if (dk == NULL) { PyErr_NoMemory(); return NULL; } } _Py_INC_REFTOTAL; dk->dk_refcnt = 1; dk->dk_size = size; dk->dk_usable = usable; dk->dk_lookup = lookdict_unicode_nodummy; dk->dk_nentries = 0; memset(&dk->dk_indices[0], 0xff, es * size); memset(DK_ENTRIES(dk), 0, sizeof(PyDictKeyEntry) * usable); return dk; } static void free_keys_object(PyDictKeysObject *keys) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n; for (i = 0, n = keys->dk_nentries; i < n; i++) { Py_XDECREF(entries[i].me_key); Py_XDECREF(entries[i].me_value); } if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) { keys_free_list[numfreekeys++] = keys; return; } PyObject_FREE(keys); } #define new_values(size) PyMem_NEW(PyObject *, size) #define free_values(values) PyMem_FREE(values) /* Consumes a reference to the keys object */ static PyObject * new_dict(PyDictKeysObject *keys, PyObject **values) { PyDictObject *mp; assert(keys != NULL); if (numfree) { mp = free_list[--numfree]; assert (mp != NULL); assert (Py_TYPE(mp) == &PyDict_Type); _Py_NewReference((PyObject *)mp); } else { mp = PyObject_GC_New(PyDictObject, &PyDict_Type); if (mp == NULL) { dictkeys_decref(keys); if (values != empty_values) { free_values(values); } return NULL; } } mp->ma_keys = keys; mp->ma_values = values; mp->ma_used = 0; mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(mp); return (PyObject *)mp; } /* Consumes a reference to the keys object */ static PyObject * new_dict_with_shared_keys(PyDictKeysObject *keys) { PyObject **values; Py_ssize_t i, size; size = USABLE_FRACTION(DK_SIZE(keys)); values = new_values(size); if (values == NULL) { dictkeys_decref(keys); return PyErr_NoMemory(); } for (i = 0; i < size; i++) { values[i] = NULL; } return new_dict(keys, values); } static PyObject * clone_combined_dict(PyDictObject *orig) { assert(PyDict_CheckExact(orig)); assert(orig->ma_values == NULL); assert(orig->ma_keys->dk_refcnt == 1); Py_ssize_t keys_size = _PyDict_KeysSize(orig->ma_keys); PyDictKeysObject *keys = PyObject_Malloc(keys_size); if (keys == NULL) { PyErr_NoMemory(); return NULL; } memcpy(keys, orig->ma_keys, keys_size); /* After copying key/value pairs, we need to incref all keys and values and they are about to be co-owned by a new dict object. */ PyDictKeyEntry *ep0 = DK_ENTRIES(keys); Py_ssize_t n = keys->dk_nentries; for (Py_ssize_t i = 0; i < n; i++) { PyDictKeyEntry *entry = &ep0[i]; PyObject *value = entry->me_value; if (value != NULL) { Py_INCREF(value); Py_INCREF(entry->me_key); } } PyDictObject *new = (PyDictObject *)new_dict(keys, NULL); if (new == NULL) { /* In case of an error, `new_dict()` takes care of cleaning up `keys`. */ return NULL; } new->ma_used = orig->ma_used; ASSERT_CONSISTENT(new); if (_PyObject_GC_IS_TRACKED(orig)) { /* Maintain tracking. */ _PyObject_GC_TRACK(new); } /* Since we copied the keys table we now have an extra reference in the system. Manually call _Py_INC_REFTOTAL to signal that we have it now; calling dictkeys_incref would be an error as keys->dk_refcnt is already set to 1 (after memcpy). */ _Py_INC_REFTOTAL; return (PyObject *)new; } PyObject * PyDict_New(void) { dictkeys_incref(Py_EMPTY_KEYS); return new_dict(Py_EMPTY_KEYS, empty_values); } /* Search index of hash table from offset of entry table */ static Py_ssize_t lookdict_index(PyDictKeysObject *k, Py_hash_t hash, Py_ssize_t index) { size_t mask = DK_MASK(k); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(k, i); if (ix == index) { return i; } if (ix == DKIX_EMPTY) { return DKIX_EMPTY; } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } /* The basic lookup function used by all operations. This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4. Open addressing is preferred over chaining since the link overhead for chaining would be substantial (100% with typical malloc overhead). The initial probe index is computed as hash mod the table size. Subsequent probe indices are computed as explained earlier. All arithmetic on hash should ignore overflow. The details in this version are due to Tim Peters, building on many past contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and Christian Tismer. lookdict() is general-purpose, and may return DKIX_ERROR if (and only if) a comparison raises an exception. lookdict_unicode() below is specialized to string keys, comparison of which can never raise an exception; that function can never return DKIX_ERROR when key is string. Otherwise, it falls back to lookdict(). lookdict_unicode_nodummy is further specialized for string keys that cannot be the value. For both, when the key isn't found a DKIX_EMPTY is returned. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { size_t i, mask, perturb; PyDictKeysObject *dk; PyDictKeyEntry *ep0; top: dk = mp->ma_keys; ep0 = DK_ENTRIES(dk); mask = DK_MASK(dk); perturb = hash; i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(dk, i); if (ix == DKIX_EMPTY) { *value_addr = NULL; return ix; } if (ix >= 0) { PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); if (ep->me_key == key) { *value_addr = ep->me_value; return ix; } if (ep->me_hash == hash) { PyObject *startkey = ep->me_key; Py_INCREF(startkey); int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); Py_DECREF(startkey); if (cmp < 0) { *value_addr = NULL; return DKIX_ERROR; } if (dk == mp->ma_keys && ep->me_key == startkey) { if (cmp > 0) { *value_addr = ep->me_value; return ix; } } else { /* The dict was mutated, restart */ goto top; } } } perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; } Py_UNREACHABLE(); } /* Specialized version for string-only keys */ static Py_ssize_t _Py_HOT_FUNCTION lookdict_unicode(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { assert(mp->ma_values == NULL); /* Make sure this function doesn't have to handle non-unicode keys, including subclasses of str; e.g., one reason to subclass unicodes is to override __eq__, and for speed we don't cater to that here. */ if (!PyUnicode_CheckExact(key)) { mp->ma_keys->dk_lookup = lookdict; return lookdict(mp, key, hash, value_addr); } PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); size_t mask = DK_MASK(mp->ma_keys); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i); if (ix == DKIX_EMPTY) { *value_addr = NULL; return DKIX_EMPTY; } if (ix >= 0) { PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { *value_addr = ep->me_value; return ix; } } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } /* Faster version of lookdict_unicode when it is known that no keys * will be present. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { assert(mp->ma_values == NULL); /* Make sure this function doesn't have to handle non-unicode keys, including subclasses of str; e.g., one reason to subclass unicodes is to override __eq__, and for speed we don't cater to that here. */ if (!PyUnicode_CheckExact(key)) { mp->ma_keys->dk_lookup = lookdict; return lookdict(mp, key, hash, value_addr); } PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); size_t mask = DK_MASK(mp->ma_keys); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i); assert (ix != DKIX_DUMMY); if (ix == DKIX_EMPTY) { *value_addr = NULL; return DKIX_EMPTY; } PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { *value_addr = ep->me_value; return ix; } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } /* Version of lookdict for split tables. * All split tables and only split tables use this lookup function. * Split tables only contain unicode keys and no dummy keys, * so algorithm is the same as lookdict_unicode_nodummy. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict_split(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { /* mp must split table */ assert(mp->ma_values != NULL); if (!PyUnicode_CheckExact(key)) { Py_ssize_t ix = lookdict(mp, key, hash, value_addr); if (ix >= 0) { *value_addr = mp->ma_values[ix]; } return ix; } PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); size_t mask = DK_MASK(mp->ma_keys); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i); assert (ix != DKIX_DUMMY); if (ix == DKIX_EMPTY) { *value_addr = NULL; return DKIX_EMPTY; } PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { *value_addr = mp->ma_values[ix]; return ix; } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } int _PyDict_HasOnlyStringKeys(PyObject *dict) { Py_ssize_t pos = 0; PyObject *key, *value; assert(PyDict_Check(dict)); /* Shortcut */ if (((PyDictObject *)dict)->ma_keys->dk_lookup != lookdict) return 1; while (PyDict_Next(dict, &pos, &key, &value)) if (!PyUnicode_Check(key)) return 0; return 1; } #define MAINTAIN_TRACKING(mp, key, value) \ do { \ if (!_PyObject_GC_IS_TRACKED(mp)) { \ if (_PyObject_GC_MAY_BE_TRACKED(key) || \ _PyObject_GC_MAY_BE_TRACKED(value)) { \ _PyObject_GC_TRACK(mp); \ } \ } \ } while(0) void _PyDict_MaybeUntrack(PyObject *op) { PyDictObject *mp; PyObject *value; Py_ssize_t i, numentries; PyDictKeyEntry *ep0; if (!PyDict_CheckExact(op) || !_PyObject_GC_IS_TRACKED(op)) return; mp = (PyDictObject *) op; ep0 = DK_ENTRIES(mp->ma_keys); numentries = mp->ma_keys->dk_nentries; if (_PyDict_HasSplitTable(mp)) { for (i = 0; i < numentries; i++) { if ((value = mp->ma_values[i]) == NULL) continue; if (_PyObject_GC_MAY_BE_TRACKED(value)) { assert(!_PyObject_GC_MAY_BE_TRACKED(ep0[i].me_key)); return; } } } else { for (i = 0; i < numentries; i++) { if ((value = ep0[i].me_value) == NULL) continue; if (_PyObject_GC_MAY_BE_TRACKED(value) || _PyObject_GC_MAY_BE_TRACKED(ep0[i].me_key)) return; } } _PyObject_GC_UNTRACK(op); } /* Internal function to find slot for an item from its hash when it is known that the key is not present in the dict. The dict must be combined. */ static Py_ssize_t find_empty_slot(PyDictKeysObject *keys, Py_hash_t hash) { assert(keys != NULL); const size_t mask = DK_MASK(keys); size_t i = hash & mask; Py_ssize_t ix = dictkeys_get_index(keys, i); for (size_t perturb = hash; ix >= 0;) { perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; ix = dictkeys_get_index(keys, i); } return i; } static int insertion_resize(PyDictObject *mp) { return dictresize(mp, GROWTH_RATE(mp)); } /* Internal routine to insert a new item into the table. Used both by the internal resize routine and by the public insert routine. Returns -1 if an error occurred, or 0 on success. */ static int insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) { PyObject *old_value; PyDictKeyEntry *ep; Py_INCREF(key); Py_INCREF(value); if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) { if (insertion_resize(mp) < 0) goto Fail; } Py_ssize_t ix = mp->ma_keys->dk_lookup(mp, key, hash, &old_value); if (ix == DKIX_ERROR) goto Fail; assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict); MAINTAIN_TRACKING(mp, key, value); /* When insertion order is different from shared key, we can't share * the key anymore. Convert this instance to combine table. */ if (_PyDict_HasSplitTable(mp) && ((ix >= 0 && old_value == NULL && mp->ma_used != ix) || (ix == DKIX_EMPTY && mp->ma_used != mp->ma_keys->dk_nentries))) { if (insertion_resize(mp) < 0) goto Fail; ix = DKIX_EMPTY; } if (ix == DKIX_EMPTY) { /* Insert into new slot. */ assert(old_value == NULL); if (mp->ma_keys->dk_usable <= 0) { /* Need to resize. */ if (insertion_resize(mp) < 0) goto Fail; } Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash); ep = &DK_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries]; dictkeys_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries); ep->me_key = key; ep->me_hash = hash; if (mp->ma_values) { assert (mp->ma_values[mp->ma_keys->dk_nentries] == NULL); mp->ma_values[mp->ma_keys->dk_nentries] = value; } else { ep->me_value = value; } mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); mp->ma_keys->dk_usable--; mp->ma_keys->dk_nentries++; assert(mp->ma_keys->dk_usable >= 0); ASSERT_CONSISTENT(mp); return 0; } if (old_value != value) { if (_PyDict_HasSplitTable(mp)) { mp->ma_values[ix] = value; if (old_value == NULL) { /* pending state */ assert(ix == mp->ma_used); mp->ma_used++; } } else { assert(old_value != NULL); DK_ENTRIES(mp->ma_keys)[ix].me_value = value; } mp->ma_version_tag = DICT_NEXT_VERSION(); } Py_XDECREF(old_value); /* which **CAN** re-enter (see issue #22653) */ ASSERT_CONSISTENT(mp); Py_DECREF(key); return 0; Fail: Py_DECREF(value); Py_DECREF(key); return -1; } // Same to insertdict but specialized for ma_keys = Py_EMPTY_KEYS. static int insert_to_emptydict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) { assert(mp->ma_keys == Py_EMPTY_KEYS); PyDictKeysObject *newkeys = new_keys_object(PyDict_MINSIZE); if (newkeys == NULL) { return -1; } if (!PyUnicode_CheckExact(key)) { newkeys->dk_lookup = lookdict; } dictkeys_decref(Py_EMPTY_KEYS); mp->ma_keys = newkeys; mp->ma_values = NULL; Py_INCREF(key); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); size_t hashpos = (size_t)hash & (PyDict_MINSIZE-1); PyDictKeyEntry *ep = DK_ENTRIES(mp->ma_keys); dictkeys_set_index(mp->ma_keys, hashpos, 0); ep->me_key = key; ep->me_hash = hash; ep->me_value = value; mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); mp->ma_keys->dk_usable--; mp->ma_keys->dk_nentries++; return 0; } /* Internal routine used by dictresize() to build a hashtable of entries. */ static void build_indices(PyDictKeysObject *keys, PyDictKeyEntry *ep, Py_ssize_t n) { size_t mask = (size_t)DK_SIZE(keys) - 1; for (Py_ssize_t ix = 0; ix != n; ix++, ep++) { Py_hash_t hash = ep->me_hash; size_t i = hash & mask; for (size_t perturb = hash; dictkeys_get_index(keys, i) != DKIX_EMPTY;) { perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } dictkeys_set_index(keys, i, ix); } } /* Restructure the table by allocating a new table and reinserting all items again. When entries have been deleted, the new table may actually be smaller than the old one. If a table is split (its keys and hashes are shared, its values are not), then the values are temporarily copied into the table, it is resized as a combined table, then the me_value slots in the old table are NULLed out. After resizing a table is always combined, but can be resplit by make_keys_shared(). */ static int dictresize(PyDictObject *mp, Py_ssize_t minsize) { Py_ssize_t newsize, numentries; PyDictKeysObject *oldkeys; PyObject **oldvalues; PyDictKeyEntry *oldentries, *newentries; /* Find the smallest table size > minused. */ for (newsize = PyDict_MINSIZE; newsize < minsize && newsize > 0; newsize <<= 1) ; if (newsize <= 0) { PyErr_NoMemory(); return -1; } oldkeys = mp->ma_keys; /* NOTE: Current odict checks mp->ma_keys to detect resize happen. * So we can't reuse oldkeys even if oldkeys->dk_size == newsize. * TODO: Try reusing oldkeys when reimplement odict. */ /* Allocate a new table. */ mp->ma_keys = new_keys_object(newsize); if (mp->ma_keys == NULL) { mp->ma_keys = oldkeys; return -1; } // New table must be large enough. assert(mp->ma_keys->dk_usable >= mp->ma_used); if (oldkeys->dk_lookup == lookdict) mp->ma_keys->dk_lookup = lookdict; numentries = mp->ma_used; oldentries = DK_ENTRIES(oldkeys); newentries = DK_ENTRIES(mp->ma_keys); oldvalues = mp->ma_values; if (oldvalues != NULL) { /* Convert split table into new combined table. * We must incref keys; we can transfer values. * Note that values of split table is always dense. */ for (Py_ssize_t i = 0; i < numentries; i++) { assert(oldvalues[i] != NULL); PyDictKeyEntry *ep = &oldentries[i]; PyObject *key = ep->me_key; Py_INCREF(key); newentries[i].me_key = key; newentries[i].me_hash = ep->me_hash; newentries[i].me_value = oldvalues[i]; } dictkeys_decref(oldkeys); mp->ma_values = NULL; if (oldvalues != empty_values) { free_values(oldvalues); } } else { // combined table. if (oldkeys->dk_nentries == numentries) { memcpy(newentries, oldentries, numentries * sizeof(PyDictKeyEntry)); } else { PyDictKeyEntry *ep = oldentries; for (Py_ssize_t i = 0; i < numentries; i++) { while (ep->me_value == NULL) ep++; newentries[i] = *ep++; } } assert(oldkeys->dk_lookup != lookdict_split); assert(oldkeys->dk_refcnt == 1); if (oldkeys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) { _Py_DEC_REFTOTAL; keys_free_list[numfreekeys++] = oldkeys; } else { _Py_DEC_REFTOTAL; PyObject_FREE(oldkeys); } } build_indices(mp->ma_keys, newentries, numentries); mp->ma_keys->dk_usable -= numentries; mp->ma_keys->dk_nentries = numentries; return 0; } /* Returns NULL if unable to split table. * A NULL return does not necessarily indicate an error */ static PyDictKeysObject * make_keys_shared(PyObject *op) { Py_ssize_t i; Py_ssize_t size; PyDictObject *mp = (PyDictObject *)op; if (!PyDict_CheckExact(op)) return NULL; if (!_PyDict_HasSplitTable(mp)) { PyDictKeyEntry *ep0; PyObject **values; assert(mp->ma_keys->dk_refcnt == 1); if (mp->ma_keys->dk_lookup == lookdict) { return NULL; } else if (mp->ma_keys->dk_lookup == lookdict_unicode) { /* Remove dummy keys */ if (dictresize(mp, DK_SIZE(mp->ma_keys))) return NULL; } assert(mp->ma_keys->dk_lookup == lookdict_unicode_nodummy); /* Copy values into a new array */ ep0 = DK_ENTRIES(mp->ma_keys); size = USABLE_FRACTION(DK_SIZE(mp->ma_keys)); values = new_values(size); if (values == NULL) { PyErr_SetString(PyExc_MemoryError, "Not enough memory to allocate new values array"); return NULL; } for (i = 0; i < size; i++) { values[i] = ep0[i].me_value; ep0[i].me_value = NULL; } mp->ma_keys->dk_lookup = lookdict_split; mp->ma_values = values; } dictkeys_incref(mp->ma_keys); return mp->ma_keys; } PyObject * _PyDict_NewPresized(Py_ssize_t minused) { const Py_ssize_t max_presize = 128 * 1024; Py_ssize_t newsize; PyDictKeysObject *new_keys; if (minused <= USABLE_FRACTION(PyDict_MINSIZE)) { return PyDict_New(); } /* There are no strict guarantee that returned dict can contain minused * items without resize. So we create medium size dict instead of very * large dict or MemoryError. */ if (minused > USABLE_FRACTION(max_presize)) { newsize = max_presize; } else { Py_ssize_t minsize = ESTIMATE_SIZE(minused); newsize = PyDict_MINSIZE*2; while (newsize < minsize) { newsize <<= 1; } } assert(IS_POWER_OF_2(newsize)); new_keys = new_keys_object(newsize); if (new_keys == NULL) return NULL; return new_dict(new_keys, NULL); } /* Note that, for historical reasons, PyDict_GetItem() suppresses all errors * that may occur (originally dicts supported only string keys, and exceptions * weren't possible). So, while the original intent was that a NULL return * meant the key wasn't present, in reality it can mean that, or that an error * (suppressed) occurred while computing the key's hash, or that some error * (suppressed) occurred when comparing keys in the dict's internal probe * sequence. A nasty example of the latter is when a Python-coded comparison * function hits a stack-depth error, which can cause this to return NULL * even if the key is present. */ PyObject * PyDict_GetItem(PyObject *op, PyObject *key) { Py_hash_t hash; Py_ssize_t ix; PyDictObject *mp = (PyDictObject *)op; PyThreadState *tstate; PyObject *value; if (!PyDict_Check(op)) return NULL; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { PyErr_Clear(); return NULL; } } /* We can arrive here with a NULL tstate during initialization: try running "python -Wi" for an example related to string interning. Let's just hope that no exception occurs then... This must be _PyThreadState_GET() and not PyThreadState_Get() because the latter abort Python if tstate is NULL. */ tstate = _PyThreadState_GET(); if (tstate != NULL && tstate->curexc_type != NULL) { /* preserve the existing exception */ PyObject *err_type, *err_value, *err_tb; PyErr_Fetch(&err_type, &err_value, &err_tb); ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); /* ignore errors */ PyErr_Restore(err_type, err_value, err_tb); if (ix < 0) return NULL; } else { ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix < 0) { PyErr_Clear(); return NULL; } } return value; } /* Same as PyDict_GetItemWithError() but with hash supplied by caller. This returns NULL *with* an exception set if an exception occurred. It returns NULL *without* an exception set if the key wasn't present. */ PyObject * _PyDict_GetItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) { Py_ssize_t ix; PyDictObject *mp = (PyDictObject *)op; PyObject *value; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix < 0) { return NULL; } return value; } /* Variant of PyDict_GetItem() that doesn't suppress exceptions. This returns NULL *with* an exception set if an exception occurred. It returns NULL *without* an exception set if the key wasn't present. */ PyObject * PyDict_GetItemWithError(PyObject *op, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyDictObject*mp = (PyDictObject *)op; PyObject *value; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { return NULL; } } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix < 0) return NULL; return value; } PyObject * _PyDict_GetItemIdWithError(PyObject *dp, struct _Py_Identifier *key) { PyObject *kv; kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) return NULL; return PyDict_GetItemWithError(dp, kv); } PyObject * _PyDict_GetItemStringWithError(PyObject *v, const char *key) { PyObject *kv, *rv; kv = PyUnicode_FromString(key); if (kv == NULL) { return NULL; } rv = PyDict_GetItemWithError(v, kv); Py_DECREF(kv); return rv; } /* Fast version of global value lookup (LOAD_GLOBAL). * Lookup in globals, then builtins. * * Raise an exception and return NULL if an error occurred (ex: computing the * key hash failed, key comparison failed, ...). Return NULL if the key doesn't * exist. Return the value if the key exists. */ PyObject * _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } /* namespace 1: globals */ ix = globals->ma_keys->dk_lookup(globals, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix != DKIX_EMPTY && value != NULL) return value; /* namespace 2: builtins */ ix = builtins->ma_keys->dk_lookup(builtins, key, hash, &value); if (ix < 0) return NULL; return value; } /* CAUTION: PyDict_SetItem() must guarantee that it won't resize the * dictionary if it's merely replacing the value for an existing key. * This means that it's safe to loop over a dictionary with PyDict_Next() * and occasionally replace a value -- but you can't insert new keys or * remove them. */ int PyDict_SetItem(PyObject *op, PyObject *key, PyObject *value) { PyDictObject *mp; Py_hash_t hash; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); assert(value); mp = (PyDictObject *)op; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; } if (mp->ma_keys == Py_EMPTY_KEYS) { return insert_to_emptydict(mp, key, hash, value); } /* insertdict() handles any resizing that might be necessary */ return insertdict(mp, key, hash, value); } int _PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value, Py_hash_t hash) { PyDictObject *mp; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); assert(value); assert(hash != -1); mp = (PyDictObject *)op; if (mp->ma_keys == Py_EMPTY_KEYS) { return insert_to_emptydict(mp, key, hash, value); } /* insertdict() handles any resizing that might be necessary */ return insertdict(mp, key, hash, value); } static int delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix, PyObject *old_value) { PyObject *old_key; PyDictKeyEntry *ep; Py_ssize_t hashpos = lookdict_index(mp->ma_keys, hash, ix); assert(hashpos >= 0); mp->ma_used--; mp->ma_version_tag = DICT_NEXT_VERSION(); ep = &DK_ENTRIES(mp->ma_keys)[ix]; dictkeys_set_index(mp->ma_keys, hashpos, DKIX_DUMMY); ENSURE_ALLOWS_DELETIONS(mp); old_key = ep->me_key; ep->me_key = NULL; ep->me_value = NULL; Py_DECREF(old_key); Py_DECREF(old_value); ASSERT_CONSISTENT(mp); return 0; } int PyDict_DelItem(PyObject *op, PyObject *key) { Py_hash_t hash; assert(key); if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; } return _PyDict_DelItem_KnownHash(op, key, hash); } int _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) { Py_ssize_t ix; PyDictObject *mp; PyObject *old_value; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); assert(hash != -1); mp = (PyDictObject *)op; ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); if (ix == DKIX_ERROR) return -1; if (ix == DKIX_EMPTY || old_value == NULL) { _PyErr_SetKeyError(key); return -1; } // Split table doesn't allow deletion. Combine it. if (_PyDict_HasSplitTable(mp)) { if (dictresize(mp, DK_SIZE(mp->ma_keys))) { return -1; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); assert(ix >= 0); } return delitem_common(mp, hash, ix, old_value); } /* This function promises that the predicate -> deletion sequence is atomic * (i.e. protected by the GIL), assuming the predicate itself doesn't * release the GIL. */ int _PyDict_DelItemIf(PyObject *op, PyObject *key, int (*predicate)(PyObject *value)) { Py_ssize_t hashpos, ix; PyDictObject *mp; Py_hash_t hash; PyObject *old_value; int res; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); hash = PyObject_Hash(key); if (hash == -1) return -1; mp = (PyDictObject *)op; ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); if (ix == DKIX_ERROR) return -1; if (ix == DKIX_EMPTY || old_value == NULL) { _PyErr_SetKeyError(key); return -1; } // Split table doesn't allow deletion. Combine it. if (_PyDict_HasSplitTable(mp)) { if (dictresize(mp, DK_SIZE(mp->ma_keys))) { return -1; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); assert(ix >= 0); } res = predicate(old_value); if (res == -1) return -1; hashpos = lookdict_index(mp->ma_keys, hash, ix); assert(hashpos >= 0); if (res > 0) return delitem_common(mp, hashpos, ix, old_value); else return 0; } void PyDict_Clear(PyObject *op) { PyDictObject *mp; PyDictKeysObject *oldkeys; PyObject **oldvalues; Py_ssize_t i, n; if (!PyDict_Check(op)) return; mp = ((PyDictObject *)op); oldkeys = mp->ma_keys; oldvalues = mp->ma_values; if (oldvalues == empty_values) return; /* Empty the dict... */ dictkeys_incref(Py_EMPTY_KEYS); mp->ma_keys = Py_EMPTY_KEYS; mp->ma_values = empty_values; mp->ma_used = 0; mp->ma_version_tag = DICT_NEXT_VERSION(); /* ...then clear the keys and values */ if (oldvalues != NULL) { n = oldkeys->dk_nentries; for (i = 0; i < n; i++) Py_CLEAR(oldvalues[i]); free_values(oldvalues); dictkeys_decref(oldkeys); } else { assert(oldkeys->dk_refcnt == 1); dictkeys_decref(oldkeys); } ASSERT_CONSISTENT(mp); } /* Internal version of PyDict_Next that returns a hash value in addition * to the key and value. * Return 1 on success, return 0 when the reached the end of the dictionary * (or if op is not a dictionary) */ int _PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue, Py_hash_t *phash) { Py_ssize_t i; PyDictObject *mp; PyDictKeyEntry *entry_ptr; PyObject *value; if (!PyDict_Check(op)) return 0; mp = (PyDictObject *)op; i = *ppos; if (mp->ma_values) { if (i < 0 || i >= mp->ma_used) return 0; /* values of split table is always dense */ entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; value = mp->ma_values[i]; assert(value != NULL); } else { Py_ssize_t n = mp->ma_keys->dk_nentries; if (i < 0 || i >= n) return 0; entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) return 0; value = entry_ptr->me_value; } *ppos = i+1; if (pkey) *pkey = entry_ptr->me_key; if (phash) *phash = entry_ptr->me_hash; if (pvalue) *pvalue = value; return 1; } /* * Iterate over a dict. Use like so: * * Py_ssize_t i; * PyObject *key, *value; * i = 0; # important! i should not otherwise be changed by you * while (PyDict_Next(yourdict, &i, &key, &value)) { * Refer to borrowed references in key and value. * } * * Return 1 on success, return 0 when the reached the end of the dictionary * (or if op is not a dictionary) * * CAUTION: In general, it isn't safe to use PyDict_Next in a loop that * mutates the dict. One exception: it is safe if the loop merely changes * the values associated with the keys (but doesn't insert new keys or * delete keys), via PyDict_SetItem(). */ int PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) { return _PyDict_Next(op, ppos, pkey, pvalue, NULL); } /* Internal version of dict.pop(). */ PyObject * _PyDict_Pop_KnownHash(PyObject *dict, PyObject *key, Py_hash_t hash, PyObject *deflt) { Py_ssize_t ix, hashpos; PyObject *old_value, *old_key; PyDictKeyEntry *ep; PyDictObject *mp; assert(PyDict_Check(dict)); mp = (PyDictObject *)dict; if (mp->ma_used == 0) { if (deflt) { Py_INCREF(deflt); return deflt; } _PyErr_SetKeyError(key); return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || old_value == NULL) { if (deflt) { Py_INCREF(deflt); return deflt; } _PyErr_SetKeyError(key); return NULL; } // Split table doesn't allow deletion. Combine it. if (_PyDict_HasSplitTable(mp)) { if (dictresize(mp, DK_SIZE(mp->ma_keys))) { return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); assert(ix >= 0); } hashpos = lookdict_index(mp->ma_keys, hash, ix); assert(hashpos >= 0); assert(old_value != NULL); mp->ma_used--; mp->ma_version_tag = DICT_NEXT_VERSION(); dictkeys_set_index(mp->ma_keys, hashpos, DKIX_DUMMY); ep = &DK_ENTRIES(mp->ma_keys)[ix]; ENSURE_ALLOWS_DELETIONS(mp); old_key = ep->me_key; ep->me_key = NULL; ep->me_value = NULL; Py_DECREF(old_key); ASSERT_CONSISTENT(mp); return old_value; } PyObject * _PyDict_Pop(PyObject *dict, PyObject *key, PyObject *deflt) { Py_hash_t hash; if (((PyDictObject *)dict)->ma_used == 0) { if (deflt) { Py_INCREF(deflt); return deflt; } _PyErr_SetKeyError(key); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } return _PyDict_Pop_KnownHash(dict, key, hash, deflt); } /* Internal version of dict.from_keys(). It is subclass-friendly. */ PyObject * _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) { PyObject *it; /* iter(iterable) */ PyObject *key; PyObject *d; int status; d = _PyObject_CallNoArg(cls); if (d == NULL) return NULL; if (PyDict_CheckExact(d) && ((PyDictObject *)d)->ma_used == 0) { if (PyDict_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; PyObject *oldvalue; Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; if (dictresize(mp, ESTIMATE_SIZE(PyDict_GET_SIZE(iterable)))) { Py_DECREF(d); return NULL; } while (_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) { if (insertdict(mp, key, hash, value)) { Py_DECREF(d); return NULL; } } return d; } if (PyAnySet_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; if (dictresize(mp, ESTIMATE_SIZE(PySet_GET_SIZE(iterable)))) { Py_DECREF(d); return NULL; } while (_PySet_NextEntry(iterable, &pos, &key, &hash)) { if (insertdict(mp, key, hash, value)) { Py_DECREF(d); return NULL; } } return d; } } it = PyObject_GetIter(iterable); if (it == NULL){ Py_DECREF(d); return NULL; } if (PyDict_CheckExact(d)) { while ((key = PyIter_Next(it)) != NULL) { status = PyDict_SetItem(d, key, value); Py_DECREF(key); if (status < 0) goto Fail; } } else { while ((key = PyIter_Next(it)) != NULL) { status = PyObject_SetItem(d, key, value); Py_DECREF(key); if (status < 0) goto Fail; } } if (PyErr_Occurred()) goto Fail; Py_DECREF(it); return d; Fail: Py_DECREF(it); Py_DECREF(d); return NULL; } /* Methods */ static void dict_dealloc(PyDictObject *mp) { PyObject **values = mp->ma_values; PyDictKeysObject *keys = mp->ma_keys; Py_ssize_t i, n; /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(mp); Py_TRASHCAN_BEGIN(mp, dict_dealloc) if (values != NULL) { if (values != empty_values) { for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) { Py_XDECREF(values[i]); } free_values(values); } dictkeys_decref(keys); } else if (keys != NULL) { assert(keys->dk_refcnt == 1); dictkeys_decref(keys); } if (numfree < PyDict_MAXFREELIST && Py_TYPE(mp) == &PyDict_Type) free_list[numfree++] = mp; else Py_TYPE(mp)->tp_free((PyObject *)mp); Py_TRASHCAN_END } static PyObject * dict_repr(PyDictObject *mp) { Py_ssize_t i; PyObject *key = NULL, *value = NULL; _PyUnicodeWriter writer; int first; i = Py_ReprEnter((PyObject *)mp); if (i != 0) { return i > 0 ? PyUnicode_FromString("{...}") : NULL; } if (mp->ma_used == 0) { Py_ReprLeave((PyObject *)mp); return PyUnicode_FromString("{}"); } _PyUnicodeWriter_Init(&writer); writer.overallocate = 1; /* "{" + "1: 2" + ", 3: 4" * (len - 1) + "}" */ writer.min_length = 1 + 4 + (2 + 4) * (mp->ma_used - 1) + 1; if (_PyUnicodeWriter_WriteChar(&writer, '{') < 0) goto error; /* Do repr() on each key+value pair, and insert ": " between them. Note that repr may mutate the dict. */ i = 0; first = 1; while (PyDict_Next((PyObject *)mp, &i, &key, &value)) { PyObject *s; int res; /* Prevent repr from deleting key or value during key format. */ Py_INCREF(key); Py_INCREF(value); if (!first) { if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0) goto error; } first = 0; s = PyObject_Repr(key); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; if (_PyUnicodeWriter_WriteASCIIString(&writer, ": ", 2) < 0) goto error; s = PyObject_Repr(value); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; Py_CLEAR(key); Py_CLEAR(value); } writer.overallocate = 0; if (_PyUnicodeWriter_WriteChar(&writer, '}') < 0) goto error; Py_ReprLeave((PyObject *)mp); return _PyUnicodeWriter_Finish(&writer); error: Py_ReprLeave((PyObject *)mp); _PyUnicodeWriter_Dealloc(&writer); Py_XDECREF(key); Py_XDECREF(value); return NULL; } static Py_ssize_t dict_length(PyDictObject *mp) { return mp->ma_used; } static PyObject * dict_subscript(PyDictObject *mp, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || value == NULL) { if (!PyDict_CheckExact(mp)) { /* Look up __missing__ method if we're a subclass. */ PyObject *missing, *res; _Py_IDENTIFIER(__missing__); missing = _PyObject_LookupSpecial((PyObject *)mp, &PyId___missing__); if (missing != NULL) { res = PyObject_CallFunctionObjArgs(missing, key, NULL); Py_DECREF(missing); return res; } else if (PyErr_Occurred()) return NULL; } _PyErr_SetKeyError(key); return NULL; } Py_INCREF(value); return value; } static int dict_ass_sub(PyDictObject *mp, PyObject *v, PyObject *w) { if (w == NULL) return PyDict_DelItem((PyObject *)mp, v); else return PyDict_SetItem((PyObject *)mp, v, w); } static PyMappingMethods dict_as_mapping = { (lenfunc)dict_length, /*mp_length*/ (binaryfunc)dict_subscript, /*mp_subscript*/ (objobjargproc)dict_ass_sub, /*mp_ass_subscript*/ }; static PyObject * dict_keys(PyDictObject *mp) { PyObject *v; Py_ssize_t i, j; PyDictKeyEntry *ep; Py_ssize_t n, offset; PyObject **value_ptr; again: n = mp->ma_used; v = PyList_New(n); if (v == NULL) return NULL; if (n != mp->ma_used) { /* Durnit. The allocations caused the dict to resize. * Just start over, this shouldn't normally happen. */ Py_DECREF(v); goto again; } ep = DK_ENTRIES(mp->ma_keys); if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *); } else { value_ptr = &ep[0].me_value; offset = sizeof(PyDictKeyEntry); } for (i = 0, j = 0; j < n; i++) { if (*value_ptr != NULL) { PyObject *key = ep[i].me_key; Py_INCREF(key); PyList_SET_ITEM(v, j, key); j++; } value_ptr = (PyObject **)(((char *)value_ptr) + offset); } assert(j == n); return v; } static PyObject * dict_values(PyDictObject *mp) { PyObject *v; Py_ssize_t i, j; PyDictKeyEntry *ep; Py_ssize_t n, offset; PyObject **value_ptr; again: n = mp->ma_used; v = PyList_New(n); if (v == NULL) return NULL; if (n != mp->ma_used) { /* Durnit. The allocations caused the dict to resize. * Just start over, this shouldn't normally happen. */ Py_DECREF(v); goto again; } ep = DK_ENTRIES(mp->ma_keys); if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *); } else { value_ptr = &ep[0].me_value; offset = sizeof(PyDictKeyEntry); } for (i = 0, j = 0; j < n; i++) { PyObject *value = *value_ptr; value_ptr = (PyObject **)(((char *)value_ptr) + offset); if (value != NULL) { Py_INCREF(value); PyList_SET_ITEM(v, j, value); j++; } } assert(j == n); return v; } static PyObject * dict_items(PyDictObject *mp) { PyObject *v; Py_ssize_t i, j, n; Py_ssize_t offset; PyObject *item, *key; PyDictKeyEntry *ep; PyObject **value_ptr; /* Preallocate the list of tuples, to avoid allocations during * the loop over the items, which could trigger GC, which * could resize the dict. :-( */ again: n = mp->ma_used; v = PyList_New(n); if (v == NULL) return NULL; for (i = 0; i < n; i++) { item = PyTuple_New(2); if (item == NULL) { Py_DECREF(v); return NULL; } PyList_SET_ITEM(v, i, item); } if (n != mp->ma_used) { /* Durnit. The allocations caused the dict to resize. * Just start over, this shouldn't normally happen. */ Py_DECREF(v); goto again; } /* Nothing we do below makes any function calls. */ ep = DK_ENTRIES(mp->ma_keys); if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *); } else { value_ptr = &ep[0].me_value; offset = sizeof(PyDictKeyEntry); } for (i = 0, j = 0; j < n; i++) { PyObject *value = *value_ptr; value_ptr = (PyObject **)(((char *)value_ptr) + offset); if (value != NULL) { key = ep[i].me_key; item = PyList_GET_ITEM(v, j); Py_INCREF(key); PyTuple_SET_ITEM(item, 0, key); Py_INCREF(value); PyTuple_SET_ITEM(item, 1, value); j++; } } assert(j == n); return v; } /*[clinic input] @classmethod dict.fromkeys iterable: object value: object=None / Create a new dictionary with keys from iterable and values set to value. [clinic start generated code]*/ static PyObject * dict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value) /*[clinic end generated code: output=8fb98e4b10384999 input=382ba4855d0f74c3]*/ { return _PyDict_FromKeys((PyObject *)type, iterable, value); } static int dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, const char *methname) { PyObject *arg = NULL; int result = 0; if (!PyArg_UnpackTuple(args, methname, 0, 1, &arg)) { result = -1; } else if (arg != NULL) { _Py_IDENTIFIER(keys); PyObject *func; if (_PyObject_LookupAttrId(arg, &PyId_keys, &func) < 0) { result = -1; } else if (func != NULL) { Py_DECREF(func); result = PyDict_Merge(self, arg, 1); } else { result = PyDict_MergeFromSeq2(self, arg, 1); } } if (result == 0 && kwds != NULL) { if (PyArg_ValidateKeywordArguments(kwds)) result = PyDict_Merge(self, kwds, 1); else result = -1; } return result; } /* Note: dict.update() uses the METH_VARARGS|METH_KEYWORDS calling convention. Using METH_FASTCALL|METH_KEYWORDS would make dict.update(**dict2) calls slower, see the issue #29312. */ static PyObject * dict_update(PyObject *self, PyObject *args, PyObject *kwds) { if (dict_update_common(self, args, kwds, "update") != -1) Py_RETURN_NONE; return NULL; } /* Update unconditionally replaces existing items. Merge has a 3rd argument 'override'; if set, it acts like Update, otherwise it leaves existing items unchanged. PyDict_{Update,Merge} update/merge from a mapping object. PyDict_MergeFromSeq2 updates/merges from any iterable object producing iterable objects of length 2. */ int PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override) { PyObject *it; /* iter(seq2) */ Py_ssize_t i; /* index into seq2 of current element */ PyObject *item; /* seq2[i] */ PyObject *fast; /* item as a 2-tuple or 2-list */ assert(d != NULL); assert(PyDict_Check(d)); assert(seq2 != NULL); it = PyObject_GetIter(seq2); if (it == NULL) return -1; for (i = 0; ; ++i) { PyObject *key, *value; Py_ssize_t n; fast = NULL; item = PyIter_Next(it); if (item == NULL) { if (PyErr_Occurred()) goto Fail; break; } /* Convert item to sequence, and verify length 2. */ fast = PySequence_Fast(item, ""); if (fast == NULL) { if (PyErr_ExceptionMatches(PyExc_TypeError)) PyErr_Format(PyExc_TypeError, "cannot convert dictionary update " "sequence element #%zd to a sequence", i); goto Fail; } n = PySequence_Fast_GET_SIZE(fast); if (n != 2) { PyErr_Format(PyExc_ValueError, "dictionary update sequence element #%zd " "has length %zd; 2 is required", i, n); goto Fail; } /* Update/merge with this (key, value) pair. */ key = PySequence_Fast_GET_ITEM(fast, 0); value = PySequence_Fast_GET_ITEM(fast, 1); Py_INCREF(key); Py_INCREF(value); if (override) { if (PyDict_SetItem(d, key, value) < 0) { Py_DECREF(key); Py_DECREF(value); goto Fail; } } else if (PyDict_GetItemWithError(d, key) == NULL) { if (PyErr_Occurred() || PyDict_SetItem(d, key, value) < 0) { Py_DECREF(key); Py_DECREF(value); goto Fail; } } Py_DECREF(key); Py_DECREF(value); Py_DECREF(fast); Py_DECREF(item); } i = 0; ASSERT_CONSISTENT(d); goto Return; Fail: Py_XDECREF(item); Py_XDECREF(fast); i = -1; Return: Py_DECREF(it); return Py_SAFE_DOWNCAST(i, Py_ssize_t, int); } static int dict_merge(PyObject *a, PyObject *b, int override) { PyDictObject *mp, *other; Py_ssize_t i, n; PyDictKeyEntry *entry, *ep0; assert(0 <= override && override <= 2); /* We accept for the argument either a concrete dictionary object, * or an abstract "mapping" object. For the former, we can do * things quite efficiently. For the latter, we only require that * PyMapping_Keys() and PyObject_GetItem() be supported. */ if (a == NULL || !PyDict_Check(a) || b == NULL) { PyErr_BadInternalCall(); return -1; } mp = (PyDictObject*)a; if (PyDict_Check(b) && (Py_TYPE(b)->tp_iter == (getiterfunc)dict_iter)) { other = (PyDictObject*)b; if (other == mp || other->ma_used == 0) /* a.update(a) or a.update({}); nothing to do */ return 0; if (mp->ma_used == 0) /* Since the target dict is empty, PyDict_GetItem() * always returns NULL. Setting override to 1 * skips the unnecessary test. */ override = 1; /* Do one big resize at the start, rather than * incrementally resizing as we insert new items. Expect * that there will be no (or few) overlapping keys. */ if (USABLE_FRACTION(mp->ma_keys->dk_size) < other->ma_used) { if (dictresize(mp, ESTIMATE_SIZE(mp->ma_used + other->ma_used))) { return -1; } } ep0 = DK_ENTRIES(other->ma_keys); for (i = 0, n = other->ma_keys->dk_nentries; i < n; i++) { PyObject *key, *value; Py_hash_t hash; entry = &ep0[i]; key = entry->me_key; hash = entry->me_hash; if (other->ma_values) value = other->ma_values[i]; else value = entry->me_value; if (value != NULL) { int err = 0; Py_INCREF(key); Py_INCREF(value); if (override == 1) err = insertdict(mp, key, hash, value); else if (_PyDict_GetItem_KnownHash(a, key, hash) == NULL) { if (PyErr_Occurred()) { Py_DECREF(value); Py_DECREF(key); return -1; } err = insertdict(mp, key, hash, value); } else if (override != 0) { _PyErr_SetKeyError(key); Py_DECREF(value); Py_DECREF(key); return -1; } Py_DECREF(value); Py_DECREF(key); if (err != 0) return -1; if (n != other->ma_keys->dk_nentries) { PyErr_SetString(PyExc_RuntimeError, "dict mutated during update"); return -1; } } } } else { /* Do it the generic, slower way */ PyObject *keys = PyMapping_Keys(b); PyObject *iter; PyObject *key, *value; int status; if (keys == NULL) /* Docstring says this is equivalent to E.keys() so * if E doesn't have a .keys() method we want * AttributeError to percolate up. Might as well * do the same for any other error. */ return -1; iter = PyObject_GetIter(keys); Py_DECREF(keys); if (iter == NULL) return -1; for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) { if (override != 1) { if (PyDict_GetItemWithError(a, key) != NULL) { if (override != 0) { _PyErr_SetKeyError(key); Py_DECREF(key); Py_DECREF(iter); return -1; } Py_DECREF(key); continue; } else if (PyErr_Occurred()) { Py_DECREF(key); Py_DECREF(iter); return -1; } } value = PyObject_GetItem(b, key); if (value == NULL) { Py_DECREF(iter); Py_DECREF(key); return -1; } status = PyDict_SetItem(a, key, value); Py_DECREF(key); Py_DECREF(value); if (status < 0) { Py_DECREF(iter); return -1; } } Py_DECREF(iter); if (PyErr_Occurred()) /* Iterator completed, via error */ return -1; } ASSERT_CONSISTENT(a); return 0; } int PyDict_Update(PyObject *a, PyObject *b) { return dict_merge(a, b, 1); } int PyDict_Merge(PyObject *a, PyObject *b, int override) { /* XXX Deprecate override not in (0, 1). */ return dict_merge(a, b, override != 0); } int _PyDict_MergeEx(PyObject *a, PyObject *b, int override) { return dict_merge(a, b, override); } static PyObject * dict_copy(PyDictObject *mp, PyObject *Py_UNUSED(ignored)) { return PyDict_Copy((PyObject*)mp); } PyObject * PyDict_Copy(PyObject *o) { PyObject *copy; PyDictObject *mp; Py_ssize_t i, n; if (o == NULL || !PyDict_Check(o)) { PyErr_BadInternalCall(); return NULL; } mp = (PyDictObject *)o; if (mp->ma_used == 0) { /* The dict is empty; just return a new dict. */ return PyDict_New(); } if (_PyDict_HasSplitTable(mp)) { PyDictObject *split_copy; Py_ssize_t size = USABLE_FRACTION(DK_SIZE(mp->ma_keys)); PyObject **newvalues; newvalues = new_values(size); if (newvalues == NULL) return PyErr_NoMemory(); split_copy = PyObject_GC_New(PyDictObject, &PyDict_Type); if (split_copy == NULL) { free_values(newvalues); return NULL; } split_copy->ma_values = newvalues; split_copy->ma_keys = mp->ma_keys; split_copy->ma_used = mp->ma_used; split_copy->ma_version_tag = DICT_NEXT_VERSION(); dictkeys_incref(mp->ma_keys); for (i = 0, n = size; i < n; i++) { PyObject *value = mp->ma_values[i]; Py_XINCREF(value); split_copy->ma_values[i] = value; } if (_PyObject_GC_IS_TRACKED(mp)) _PyObject_GC_TRACK(split_copy); return (PyObject *)split_copy; } if (PyDict_CheckExact(mp) && mp->ma_values == NULL && (mp->ma_used >= (mp->ma_keys->dk_nentries * 2) / 3)) { /* Use fast-copy if: (1) 'mp' is an instance of a subclassed dict; and (2) 'mp' is not a split-dict; and (3) if 'mp' is non-compact ('del' operation does not resize dicts), do fast-copy only if it has at most 1/3 non-used keys. The last condition (3) is important to guard against a pathological case when a large dict is almost emptied with multiple del/pop operations and copied after that. In cases like this, we defer to PyDict_Merge, which produces a compacted copy. */ return clone_combined_dict(mp); } copy = PyDict_New(); if (copy == NULL) return NULL; if (PyDict_Merge(copy, o, 1) == 0) return copy; Py_DECREF(copy); return NULL; } Py_ssize_t PyDict_Size(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return -1; } return ((PyDictObject *)mp)->ma_used; } PyObject * PyDict_Keys(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return NULL; } return dict_keys((PyDictObject *)mp); } PyObject * PyDict_Values(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return NULL; } return dict_values((PyDictObject *)mp); } PyObject * PyDict_Items(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return NULL; } return dict_items((PyDictObject *)mp); } /* Return 1 if dicts equal, 0 if not, -1 if error. * Gets out as soon as any difference is detected. * Uses only Py_EQ comparison. */ static int dict_equal(PyDictObject *a, PyDictObject *b) { Py_ssize_t i; if (a->ma_used != b->ma_used) /* can't be equal if # of entries differ */ return 0; /* Same # of entries -- check all of 'em. Exit early on any diff. */ for (i = 0; i < a->ma_keys->dk_nentries; i++) { PyDictKeyEntry *ep = &DK_ENTRIES(a->ma_keys)[i]; PyObject *aval; if (a->ma_values) aval = a->ma_values[i]; else aval = ep->me_value; if (aval != NULL) { int cmp; PyObject *bval; PyObject *key = ep->me_key; /* temporarily bump aval's refcount to ensure it stays alive until we're done with it */ Py_INCREF(aval); /* ditto for key */ Py_INCREF(key); /* reuse the known hash value */ b->ma_keys->dk_lookup(b, key, ep->me_hash, &bval); if (bval == NULL) { Py_DECREF(key); Py_DECREF(aval); if (PyErr_Occurred()) return -1; return 0; } Py_INCREF(bval); cmp = PyObject_RichCompareBool(aval, bval, Py_EQ); Py_DECREF(key); Py_DECREF(aval); Py_DECREF(bval); if (cmp <= 0) /* error or not equal */ return cmp; } } return 1; } static PyObject * dict_richcompare(PyObject *v, PyObject *w, int op) { int cmp; PyObject *res; if (!PyDict_Check(v) || !PyDict_Check(w)) { res = Py_NotImplemented; } else if (op == Py_EQ || op == Py_NE) { cmp = dict_equal((PyDictObject *)v, (PyDictObject *)w); if (cmp < 0) return NULL; res = (cmp == (op == Py_EQ)) ? Py_True : Py_False; } else res = Py_NotImplemented; Py_INCREF(res); return res; } /*[clinic input] @coexist dict.__contains__ key: object / True if the dictionary has the specified key, else False. [clinic start generated code]*/ static PyObject * dict___contains__(PyDictObject *self, PyObject *key) /*[clinic end generated code: output=a3d03db709ed6e6b input=fe1cb42ad831e820]*/ { register PyDictObject *mp = self; Py_hash_t hash; Py_ssize_t ix; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || value == NULL) Py_RETURN_FALSE; Py_RETURN_TRUE; } /*[clinic input] dict.get key: object default: object = None / Return the value for key if key is in the dictionary, else default. [clinic start generated code]*/ static PyObject * dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value) /*[clinic end generated code: output=bba707729dee05bf input=279ddb5790b6b107]*/ { PyObject *val = NULL; Py_hash_t hash; Py_ssize_t ix; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (self->ma_keys->dk_lookup) (self, key, hash, &val); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || val == NULL) { val = default_value; } Py_INCREF(val); return val; } PyObject * PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj) { PyDictObject *mp = (PyDictObject *)d; PyObject *value; Py_hash_t hash; if (!PyDict_Check(d)) { PyErr_BadInternalCall(); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } if (mp->ma_keys == Py_EMPTY_KEYS) { if (insert_to_emptydict(mp, key, hash, defaultobj) < 0) { return NULL; } return defaultobj; } if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) { if (insertion_resize(mp) < 0) return NULL; } Py_ssize_t ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (_PyDict_HasSplitTable(mp) && ((ix >= 0 && value == NULL && mp->ma_used != ix) || (ix == DKIX_EMPTY && mp->ma_used != mp->ma_keys->dk_nentries))) { if (insertion_resize(mp) < 0) { return NULL; } ix = DKIX_EMPTY; } if (ix == DKIX_EMPTY) { PyDictKeyEntry *ep, *ep0; value = defaultobj; if (mp->ma_keys->dk_usable <= 0) { if (insertion_resize(mp) < 0) { return NULL; } } Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash); ep0 = DK_ENTRIES(mp->ma_keys); ep = &ep0[mp->ma_keys->dk_nentries]; dictkeys_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries); Py_INCREF(key); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); ep->me_key = key; ep->me_hash = hash; if (_PyDict_HasSplitTable(mp)) { assert(mp->ma_values[mp->ma_keys->dk_nentries] == NULL); mp->ma_values[mp->ma_keys->dk_nentries] = value; } else { ep->me_value = value; } mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); mp->ma_keys->dk_usable--; mp->ma_keys->dk_nentries++; assert(mp->ma_keys->dk_usable >= 0); } else if (value == NULL) { value = defaultobj; assert(_PyDict_HasSplitTable(mp)); assert(ix == mp->ma_used); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); mp->ma_values[ix] = value; mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); } ASSERT_CONSISTENT(mp); return value; } /*[clinic input] dict.setdefault key: object default: object = None / Insert key with a value of default if key is not in the dictionary. Return the value for key if key is in the dictionary, else default. [clinic start generated code]*/ static PyObject * dict_setdefault_impl(PyDictObject *self, PyObject *key, PyObject *default_value) /*[clinic end generated code: output=f8c1101ebf69e220 input=0f063756e815fd9d]*/ { PyObject *val; val = PyDict_SetDefault((PyObject *)self, key, default_value); Py_XINCREF(val); return val; } static PyObject * dict_clear(PyDictObject *mp, PyObject *Py_UNUSED(ignored)) { PyDict_Clear((PyObject *)mp); Py_RETURN_NONE; } /* We don't use Argument Clinic for dict.pop because it doesn't support custom signature for now. */ PyDoc_STRVAR(dict_pop__doc__, "D.pop(k[,d]) -> v, remove specified key and return the corresponding value.\n\ If key is not found, d is returned if given, otherwise KeyError is raised"); #define DICT_POP_METHODDEF \ {"pop", (PyCFunction)(void(*)(void))dict_pop, METH_FASTCALL, dict_pop__doc__}, static PyObject * dict_pop(PyDictObject *self, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; PyObject *key; PyObject *default_value = NULL; if (!_PyArg_CheckPositional("pop", nargs, 1, 2)) { goto exit; } key = args[0]; if (nargs < 2) { goto skip_optional; } default_value = args[1]; skip_optional: return_value = _PyDict_Pop((PyObject*)self, key, default_value); exit: return return_value; } /*[clinic input] dict.popitem Remove and return a (key, value) pair as a 2-tuple. Pairs are returned in LIFO (last-in, first-out) order. Raises KeyError if the dict is empty. [clinic start generated code]*/ static PyObject * dict_popitem_impl(PyDictObject *self) /*[clinic end generated code: output=e65fcb04420d230d input=1c38a49f21f64941]*/ { Py_ssize_t i, j; PyDictKeyEntry *ep0, *ep; PyObject *res; /* Allocate the result tuple before checking the size. Believe it * or not, this allocation could trigger a garbage collection which * could empty the dict, so if we checked the size first and that * happened, the result would be an infinite loop (searching for an * entry that no longer exists). Note that the usual popitem() * idiom is "while d: k, v = d.popitem()". so needing to throw the * tuple away if the dict *is* empty isn't a significant * inefficiency -- possible, but unlikely in practice. */ res = PyTuple_New(2); if (res == NULL) return NULL; if (self->ma_used == 0) { Py_DECREF(res); PyErr_SetString(PyExc_KeyError, "popitem(): dictionary is empty"); return NULL; } /* Convert split table to combined table */ if (self->ma_keys->dk_lookup == lookdict_split) { if (dictresize(self, DK_SIZE(self->ma_keys))) { Py_DECREF(res); return NULL; } } ENSURE_ALLOWS_DELETIONS(self); /* Pop last item */ ep0 = DK_ENTRIES(self->ma_keys); i = self->ma_keys->dk_nentries - 1; while (i >= 0 && ep0[i].me_value == NULL) { i--; } assert(i >= 0); ep = &ep0[i]; j = lookdict_index(self->ma_keys, ep->me_hash, i); assert(j >= 0); assert(dictkeys_get_index(self->ma_keys, j) == i); dictkeys_set_index(self->ma_keys, j, DKIX_DUMMY); PyTuple_SET_ITEM(res, 0, ep->me_key); PyTuple_SET_ITEM(res, 1, ep->me_value); ep->me_key = NULL; ep->me_value = NULL; /* We can't dk_usable++ since there is DKIX_DUMMY in indices */ self->ma_keys->dk_nentries = i; self->ma_used--; self->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(self); return res; } static int dict_traverse(PyObject *op, visitproc visit, void *arg) { PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n = keys->dk_nentries; if (keys->dk_lookup == lookdict) { for (i = 0; i < n; i++) { if (entries[i].me_value != NULL) { Py_VISIT(entries[i].me_value); Py_VISIT(entries[i].me_key); } } } else { if (mp->ma_values != NULL) { for (i = 0; i < n; i++) { Py_VISIT(mp->ma_values[i]); } } else { for (i = 0; i < n; i++) { Py_VISIT(entries[i].me_value); } } } return 0; } static int dict_tp_clear(PyObject *op) { PyDict_Clear(op); return 0; } static PyObject *dictiter_new(PyDictObject *, PyTypeObject *); Py_ssize_t _PyDict_SizeOf(PyDictObject *mp) { Py_ssize_t size, usable, res; size = DK_SIZE(mp->ma_keys); usable = USABLE_FRACTION(size); res = _PyObject_SIZE(Py_TYPE(mp)); if (mp->ma_values) res += usable * sizeof(PyObject*); /* If the dictionary is split, the keys portion is accounted-for in the type object. */ if (mp->ma_keys->dk_refcnt == 1) res += (sizeof(PyDictKeysObject) + DK_IXSIZE(mp->ma_keys) * size + sizeof(PyDictKeyEntry) * usable); return res; } Py_ssize_t _PyDict_KeysSize(PyDictKeysObject *keys) { return (sizeof(PyDictKeysObject) + DK_IXSIZE(keys) * DK_SIZE(keys) + USABLE_FRACTION(DK_SIZE(keys)) * sizeof(PyDictKeyEntry)); } static PyObject * dict_sizeof(PyDictObject *mp, PyObject *Py_UNUSED(ignored)) { return PyLong_FromSsize_t(_PyDict_SizeOf(mp)); } PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]"); PyDoc_STRVAR(sizeof__doc__, "D.__sizeof__() -> size of D in memory, in bytes"); PyDoc_STRVAR(update__doc__, "D.update([E, ]**F) -> None. Update D from dict/iterable E and F.\n\ If E is present and has a .keys() method, then does: for k in E: D[k] = E[k]\n\ If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v\n\ In either case, this is followed by: for k in F: D[k] = F[k]"); PyDoc_STRVAR(clear__doc__, "D.clear() -> None. Remove all items from D."); PyDoc_STRVAR(copy__doc__, "D.copy() -> a shallow copy of D"); /* Forward */ static PyObject *dictkeys_new(PyObject *, PyObject *); static PyObject *dictitems_new(PyObject *, PyObject *); static PyObject *dictvalues_new(PyObject *, PyObject *); PyDoc_STRVAR(keys__doc__, "D.keys() -> a set-like object providing a view on D's keys"); PyDoc_STRVAR(items__doc__, "D.items() -> a set-like object providing a view on D's items"); PyDoc_STRVAR(values__doc__, "D.values() -> an object providing a view on D's values"); static PyMethodDef mapp_methods[] = { DICT___CONTAINS___METHODDEF {"__getitem__", (PyCFunction)(void(*)(void))dict_subscript, METH_O | METH_COEXIST, getitem__doc__}, {"__sizeof__", (PyCFunction)(void(*)(void))dict_sizeof, METH_NOARGS, sizeof__doc__}, DICT_GET_METHODDEF DICT_SETDEFAULT_METHODDEF DICT_POP_METHODDEF DICT_POPITEM_METHODDEF {"keys", dictkeys_new, METH_NOARGS, keys__doc__}, {"items", dictitems_new, METH_NOARGS, items__doc__}, {"values", dictvalues_new, METH_NOARGS, values__doc__}, {"update", (PyCFunction)(void(*)(void))dict_update, METH_VARARGS | METH_KEYWORDS, update__doc__}, DICT_FROMKEYS_METHODDEF {"clear", (PyCFunction)dict_clear, METH_NOARGS, clear__doc__}, {"copy", (PyCFunction)dict_copy, METH_NOARGS, copy__doc__}, DICT___REVERSED___METHODDEF {NULL, NULL} /* sentinel */ }; /* Return 1 if `key` is in dict `op`, 0 if not, and -1 on error. */ int PyDict_Contains(PyObject *op, PyObject *key) { Py_hash_t hash; Py_ssize_t ix; PyDictObject *mp = (PyDictObject *)op; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return -1; return (ix != DKIX_EMPTY && value != NULL); } /* Internal version of PyDict_Contains used when the hash value is already known */ int _PyDict_Contains(PyObject *op, PyObject *key, Py_hash_t hash) { PyDictObject *mp = (PyDictObject *)op; PyObject *value; Py_ssize_t ix; ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return -1; return (ix != DKIX_EMPTY && value != NULL); } /* Hack to implement "key in dict" */ static PySequenceMethods dict_as_sequence = { 0, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ PyDict_Contains, /* sq_contains */ 0, /* sq_inplace_concat */ 0, /* sq_inplace_repeat */ }; static PyObject * dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *self; PyDictObject *d; assert(type != NULL && type->tp_alloc != NULL); self = type->tp_alloc(type, 0); if (self == NULL) return NULL; d = (PyDictObject *)self; /* The object has been implicitly tracked by tp_alloc */ if (type == &PyDict_Type) _PyObject_GC_UNTRACK(d); d->ma_used = 0; d->ma_version_tag = DICT_NEXT_VERSION(); d->ma_keys = new_keys_object(PyDict_MINSIZE); if (d->ma_keys == NULL) { Py_DECREF(self); return NULL; } ASSERT_CONSISTENT(d); return self; } static int dict_init(PyObject *self, PyObject *args, PyObject *kwds) { return dict_update_common(self, args, kwds, "dict"); } static PyObject * dict_iter(PyDictObject *dict) { return dictiter_new(dict, &PyDictIterKey_Type); } PyDoc_STRVAR(dictionary_doc, "dict() -> new empty dictionary\n" "dict(mapping) -> new dictionary initialized from a mapping object's\n" " (key, value) pairs\n" "dict(iterable) -> new dictionary initialized as if via:\n" " d = {}\n" " for k, v in iterable:\n" " d[k] = v\n" "dict(**kwargs) -> new dictionary initialized with the name=value pairs\n" " in the keyword argument list. For example: dict(one=1, two=2)"); PyTypeObject PyDict_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict", sizeof(PyDictObject), 0, (destructor)dict_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dict_repr, /* tp_repr */ 0, /* tp_as_number */ &dict_as_sequence, /* tp_as_sequence */ &dict_as_mapping, /* tp_as_mapping */ PyObject_HashNotImplemented, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_DICT_SUBCLASS, /* tp_flags */ dictionary_doc, /* tp_doc */ dict_traverse, /* tp_traverse */ dict_tp_clear, /* tp_clear */ dict_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dict_iter, /* tp_iter */ 0, /* tp_iternext */ mapp_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ dict_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ dict_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ }; PyObject * _PyDict_GetItemId(PyObject *dp, struct _Py_Identifier *key) { PyObject *kv; kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) { PyErr_Clear(); return NULL; } return PyDict_GetItem(dp, kv); } /* For backward compatibility with old dictionary interface */ PyObject * PyDict_GetItemString(PyObject *v, const char *key) { PyObject *kv, *rv; kv = PyUnicode_FromString(key); if (kv == NULL) { PyErr_Clear(); return NULL; } rv = PyDict_GetItem(v, kv); Py_DECREF(kv); return rv; } int _PyDict_SetItemId(PyObject *v, struct _Py_Identifier *key, PyObject *item) { PyObject *kv; kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) return -1; return PyDict_SetItem(v, kv, item); } int PyDict_SetItemString(PyObject *v, const char *key, PyObject *item) { PyObject *kv; int err; kv = PyUnicode_FromString(key); if (kv == NULL) return -1; PyUnicode_InternInPlace(&kv); /* XXX Should we really? */ err = PyDict_SetItem(v, kv, item); Py_DECREF(kv); return err; } int _PyDict_DelItemId(PyObject *v, _Py_Identifier *key) { PyObject *kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) return -1; return PyDict_DelItem(v, kv); } int PyDict_DelItemString(PyObject *v, const char *key) { PyObject *kv; int err; kv = PyUnicode_FromString(key); if (kv == NULL) return -1; err = PyDict_DelItem(v, kv); Py_DECREF(kv); return err; } /* Dictionary iterator types */ typedef struct { PyObject_HEAD PyDictObject *di_dict; /* Set to NULL when iterator is exhausted */ Py_ssize_t di_used; Py_ssize_t di_pos; PyObject* di_result; /* reusable result tuple for iteritems */ Py_ssize_t len; } dictiterobject; static PyObject * dictiter_new(PyDictObject *dict, PyTypeObject *itertype) { dictiterobject *di; di = PyObject_GC_New(dictiterobject, itertype); if (di == NULL) { return NULL; } Py_INCREF(dict); di->di_dict = dict; di->di_used = dict->ma_used; di->len = dict->ma_used; if (itertype == &PyDictRevIterKey_Type || itertype == &PyDictRevIterItem_Type || itertype == &PyDictRevIterValue_Type) { if (dict->ma_values) { di->di_pos = dict->ma_used - 1; } else { di->di_pos = dict->ma_keys->dk_nentries - 1; } } else { di->di_pos = 0; } if (itertype == &PyDictIterItem_Type || itertype == &PyDictRevIterItem_Type) { di->di_result = PyTuple_Pack(2, Py_None, Py_None); if (di->di_result == NULL) { Py_DECREF(di); return NULL; } } else { di->di_result = NULL; } _PyObject_GC_TRACK(di); return (PyObject *)di; } static void dictiter_dealloc(dictiterobject *di) { /* bpo-31095: UnTrack is needed before calling any callbacks */ _PyObject_GC_UNTRACK(di); Py_XDECREF(di->di_dict); Py_XDECREF(di->di_result); PyObject_GC_Del(di); } static int dictiter_traverse(dictiterobject *di, visitproc visit, void *arg) { Py_VISIT(di->di_dict); Py_VISIT(di->di_result); return 0; } static PyObject * dictiter_len(dictiterobject *di, PyObject *Py_UNUSED(ignored)) { Py_ssize_t len = 0; if (di->di_dict != NULL && di->di_used == di->di_dict->ma_used) len = di->len; return PyLong_FromSize_t(len); } PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); static PyObject * dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyMethodDef dictiter_methods[] = { {"__length_hint__", (PyCFunction)(void(*)(void))dictiter_len, METH_NOARGS, length_hint_doc}, {"__reduce__", (PyCFunction)(void(*)(void))dictiter_reduce, METH_NOARGS, reduce_doc}, {NULL, NULL} /* sentinel */ }; static PyObject* dictiter_iternextkey(dictiterobject *di) { PyObject *key; Py_ssize_t i; PyDictKeysObject *k; PyDictObject *d = di->di_dict; if (d == NULL) return NULL; assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } i = di->di_pos; k = d->ma_keys; assert(i >= 0); if (d->ma_values) { if (i >= d->ma_used) goto fail; key = DK_ENTRIES(k)[i].me_key; assert(d->ma_values[i] != NULL); } else { Py_ssize_t n = k->dk_nentries; PyDictKeyEntry *entry_ptr = &DK_ENTRIES(k)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) goto fail; key = entry_ptr->me_key; } // We found an element (key), but did not expect it if (di->len == 0) { PyErr_SetString(PyExc_RuntimeError, "dictionary keys changed during iteration"); goto fail; } di->di_pos = i+1; di->len--; Py_INCREF(key); return key; fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictIterKey_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_keyiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)dictiter_iternextkey, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject * dictiter_iternextvalue(dictiterobject *di) { PyObject *value; Py_ssize_t i; PyDictObject *d = di->di_dict; if (d == NULL) return NULL; assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } i = di->di_pos; assert(i >= 0); if (d->ma_values) { if (i >= d->ma_used) goto fail; value = d->ma_values[i]; assert(value != NULL); } else { Py_ssize_t n = d->ma_keys->dk_nentries; PyDictKeyEntry *entry_ptr = &DK_ENTRIES(d->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) goto fail; value = entry_ptr->me_value; } // We found an element, but did not expect it if (di->len == 0) { PyErr_SetString(PyExc_RuntimeError, "dictionary keys changed during iteration"); goto fail; } di->di_pos = i+1; di->len--; Py_INCREF(value); return value; fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictIterValue_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_valueiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)dictiter_iternextvalue, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject * dictiter_iternextitem(dictiterobject *di) { PyObject *key, *value, *result; Py_ssize_t i; PyDictObject *d = di->di_dict; if (d == NULL) return NULL; assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } i = di->di_pos; assert(i >= 0); if (d->ma_values) { if (i >= d->ma_used) goto fail; key = DK_ENTRIES(d->ma_keys)[i].me_key; value = d->ma_values[i]; assert(value != NULL); } else { Py_ssize_t n = d->ma_keys->dk_nentries; PyDictKeyEntry *entry_ptr = &DK_ENTRIES(d->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) goto fail; key = entry_ptr->me_key; value = entry_ptr->me_value; } // We found an element, but did not expect it if (di->len == 0) { PyErr_SetString(PyExc_RuntimeError, "dictionary keys changed during iteration"); goto fail; } di->di_pos = i+1; di->len--; Py_INCREF(key); Py_INCREF(value); result = di->di_result; if (Py_REFCNT(result) == 1) { PyObject *oldkey = PyTuple_GET_ITEM(result, 0); PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); } else { result = PyTuple_New(2); if (result == NULL) return NULL; PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ } return result; fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictIterItem_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_itemiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)dictiter_iternextitem, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; /* dictreviter */ static PyObject * dictreviter_iternext(dictiterobject *di) { PyDictObject *d = di->di_dict; if (d == NULL) { return NULL; } assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } Py_ssize_t i = di->di_pos; PyDictKeysObject *k = d->ma_keys; PyObject *key, *value, *result; if (i < 0) { goto fail; } if (d->ma_values) { key = DK_ENTRIES(k)[i].me_key; value = d->ma_values[i]; assert (value != NULL); } else { PyDictKeyEntry *entry_ptr = &DK_ENTRIES(k)[i]; while (entry_ptr->me_value == NULL) { if (--i < 0) { goto fail; } entry_ptr--; } key = entry_ptr->me_key; value = entry_ptr->me_value; } di->di_pos = i-1; di->len--; if (Py_TYPE(di) == &PyDictRevIterKey_Type) { Py_INCREF(key); return key; } else if (Py_TYPE(di) == &PyDictRevIterValue_Type) { Py_INCREF(value); return value; } else if (Py_TYPE(di) == &PyDictRevIterItem_Type) { Py_INCREF(key); Py_INCREF(value); result = di->di_result; if (Py_REFCNT(result) == 1) { PyObject *oldkey = PyTuple_GET_ITEM(result, 0); PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); } else { result = PyTuple_New(2); if (result == NULL) { return NULL; } PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ } return result; } else { Py_UNREACHABLE(); } fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictRevIterKey_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_reversekeyiterator", sizeof(dictiterobject), .tp_dealloc = (destructor)dictiter_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_traverse = (traverseproc)dictiter_traverse, .tp_iter = PyObject_SelfIter, .tp_iternext = (iternextfunc)dictreviter_iternext, .tp_methods = dictiter_methods }; /*[clinic input] dict.__reversed__ Return a reverse iterator over the dict keys. [clinic start generated code]*/ static PyObject * dict___reversed___impl(PyDictObject *self) /*[clinic end generated code: output=e674483336d1ed51 input=23210ef3477d8c4d]*/ { assert (PyDict_Check(self)); return dictiter_new(self, &PyDictRevIterKey_Type); } static PyObject * dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)) { _Py_IDENTIFIER(iter); /* copy the iterator state */ dictiterobject tmp = *di; Py_XINCREF(tmp.di_dict); PyObject *list = PySequence_List((PyObject*)&tmp); Py_XDECREF(tmp.di_dict); if (list == NULL) { return NULL; } return Py_BuildValue("N(N)", _PyEval_GetBuiltinId(&PyId_iter), list); } PyTypeObject PyDictRevIterItem_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_reverseitemiterator", sizeof(dictiterobject), .tp_dealloc = (destructor)dictiter_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_traverse = (traverseproc)dictiter_traverse, .tp_iter = PyObject_SelfIter, .tp_iternext = (iternextfunc)dictreviter_iternext, .tp_methods = dictiter_methods }; PyTypeObject PyDictRevIterValue_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_reversevalueiterator", sizeof(dictiterobject), .tp_dealloc = (destructor)dictiter_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_traverse = (traverseproc)dictiter_traverse, .tp_iter = PyObject_SelfIter, .tp_iternext = (iternextfunc)dictreviter_iternext, .tp_methods = dictiter_methods }; /***********************************************/ /* View objects for keys(), items(), values(). */ /***********************************************/ /* The instance lay-out is the same for all three; but the type differs. */ static void dictview_dealloc(_PyDictViewObject *dv) { /* bpo-31095: UnTrack is needed before calling any callbacks */ _PyObject_GC_UNTRACK(dv); Py_XDECREF(dv->dv_dict); PyObject_GC_Del(dv); } static int dictview_traverse(_PyDictViewObject *dv, visitproc visit, void *arg) { Py_VISIT(dv->dv_dict); return 0; } static Py_ssize_t dictview_len(_PyDictViewObject *dv) { Py_ssize_t len = 0; if (dv->dv_dict != NULL) len = dv->dv_dict->ma_used; return len; } PyObject * _PyDictView_New(PyObject *dict, PyTypeObject *type) { _PyDictViewObject *dv; if (dict == NULL) { PyErr_BadInternalCall(); return NULL; } if (!PyDict_Check(dict)) { /* XXX Get rid of this restriction later */ PyErr_Format(PyExc_TypeError, "%s() requires a dict argument, not '%s'", type->tp_name, dict->ob_type->tp_name); return NULL; } dv = PyObject_GC_New(_PyDictViewObject, type); if (dv == NULL) return NULL; Py_INCREF(dict); dv->dv_dict = (PyDictObject *)dict; _PyObject_GC_TRACK(dv); return (PyObject *)dv; } /* TODO(guido): The views objects are not complete: * support more set operations * support arbitrary mappings? - either these should be static or exported in dictobject.h - if public then they should probably be in builtins */ /* Return 1 if self is a subset of other, iterating over self; 0 if not; -1 if an error occurred. */ static int all_contained_in(PyObject *self, PyObject *other) { PyObject *iter = PyObject_GetIter(self); int ok = 1; if (iter == NULL) return -1; for (;;) { PyObject *next = PyIter_Next(iter); if (next == NULL) { if (PyErr_Occurred()) ok = -1; break; } ok = PySequence_Contains(other, next); Py_DECREF(next); if (ok <= 0) break; } Py_DECREF(iter); return ok; } static PyObject * dictview_richcompare(PyObject *self, PyObject *other, int op) { Py_ssize_t len_self, len_other; int ok; PyObject *result; assert(self != NULL); assert(PyDictViewSet_Check(self)); assert(other != NULL); if (!PyAnySet_Check(other) && !PyDictViewSet_Check(other)) Py_RETURN_NOTIMPLEMENTED; len_self = PyObject_Size(self); if (len_self < 0) return NULL; len_other = PyObject_Size(other); if (len_other < 0) return NULL; ok = 0; switch(op) { case Py_NE: case Py_EQ: if (len_self == len_other) ok = all_contained_in(self, other); if (op == Py_NE && ok >= 0) ok = !ok; break; case Py_LT: if (len_self < len_other) ok = all_contained_in(self, other); break; case Py_LE: if (len_self <= len_other) ok = all_contained_in(self, other); break; case Py_GT: if (len_self > len_other) ok = all_contained_in(other, self); break; case Py_GE: if (len_self >= len_other) ok = all_contained_in(other, self); break; } if (ok < 0) return NULL; result = ok ? Py_True : Py_False; Py_INCREF(result); return result; } static PyObject * dictview_repr(_PyDictViewObject *dv) { PyObject *seq; PyObject *result = NULL; Py_ssize_t rc; rc = Py_ReprEnter((PyObject *)dv); if (rc != 0) { return rc > 0 ? PyUnicode_FromString("...") : NULL; } seq = PySequence_List((PyObject *)dv); if (seq == NULL) { goto Done; } result = PyUnicode_FromFormat("%s(%R)", Py_TYPE(dv)->tp_name, seq); Py_DECREF(seq); Done: Py_ReprLeave((PyObject *)dv); return result; } /*** dict_keys ***/ static PyObject * dictkeys_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictIterKey_Type); } static int dictkeys_contains(_PyDictViewObject *dv, PyObject *obj) { if (dv->dv_dict == NULL) return 0; return PyDict_Contains((PyObject *)dv->dv_dict, obj); } static PySequenceMethods dictkeys_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictkeys_contains, /* sq_contains */ }; static PyObject* dictviews_sub(PyObject* self, PyObject *other) { PyObject *result = PySet_New(self); PyObject *tmp; _Py_IDENTIFIER(difference_update); if (result == NULL) return NULL; tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_difference_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } PyObject* _PyDictView_Intersect(PyObject* self, PyObject *other) { PyObject *result = PySet_New(self); PyObject *tmp; _Py_IDENTIFIER(intersection_update); if (result == NULL) return NULL; tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_intersection_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static PyObject* dictviews_or(PyObject* self, PyObject *other) { PyObject *result = PySet_New(self); PyObject *tmp; _Py_IDENTIFIER(update); if (result == NULL) return NULL; tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static PyObject* dictviews_xor(PyObject* self, PyObject *other) { PyObject *result = PySet_New(self); PyObject *tmp; _Py_IDENTIFIER(symmetric_difference_update); if (result == NULL) return NULL; tmp = _PyObject_CallMethodIdObjArgs(result, &PyId_symmetric_difference_update, other, NULL); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static PyNumberMethods dictviews_as_number = { 0, /*nb_add*/ (binaryfunc)dictviews_sub, /*nb_subtract*/ 0, /*nb_multiply*/ 0, /*nb_remainder*/ 0, /*nb_divmod*/ 0, /*nb_power*/ 0, /*nb_negative*/ 0, /*nb_positive*/ 0, /*nb_absolute*/ 0, /*nb_bool*/ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ (binaryfunc)_PyDictView_Intersect, /*nb_and*/ (binaryfunc)dictviews_xor, /*nb_xor*/ (binaryfunc)dictviews_or, /*nb_or*/ }; static PyObject* dictviews_isdisjoint(PyObject *self, PyObject *other) { PyObject *it; PyObject *item = NULL; if (self == other) { if (dictview_len((_PyDictViewObject *)self) == 0) Py_RETURN_TRUE; else Py_RETURN_FALSE; } /* Iterate over the shorter object (only if other is a set, * because PySequence_Contains may be expensive otherwise): */ if (PyAnySet_Check(other) || PyDictViewSet_Check(other)) { Py_ssize_t len_self = dictview_len((_PyDictViewObject *)self); Py_ssize_t len_other = PyObject_Size(other); if (len_other == -1) return NULL; if ((len_other > len_self)) { PyObject *tmp = other; other = self; self = tmp; } } it = PyObject_GetIter(other); if (it == NULL) return NULL; while ((item = PyIter_Next(it)) != NULL) { int contains = PySequence_Contains(self, item); Py_DECREF(item); if (contains == -1) { Py_DECREF(it); return NULL; } if (contains) { Py_DECREF(it); Py_RETURN_FALSE; } } Py_DECREF(it); if (PyErr_Occurred()) return NULL; /* PyIter_Next raised an exception. */ Py_RETURN_TRUE; } PyDoc_STRVAR(isdisjoint_doc, "Return True if the view and the given iterable have a null intersection."); static PyObject* dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_keys_doc, "Return a reverse iterator over the dict keys."); static PyMethodDef dictkeys_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {"__reversed__", (PyCFunction)(void(*)(void))dictkeys_reversed, METH_NOARGS, reversed_keys_doc}, {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictKeys_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_keys", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictkeys_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dictkeys_iter, /* tp_iter */ 0, /* tp_iternext */ dictkeys_methods, /* tp_methods */ 0, }; static PyObject * dictkeys_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _PyDictView_New(dict, &PyDictKeys_Type); } static PyObject * dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterKey_Type); } /*** dict_items ***/ static PyObject * dictitems_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictIterItem_Type); } static int dictitems_contains(_PyDictViewObject *dv, PyObject *obj) { int result; PyObject *key, *value, *found; if (dv->dv_dict == NULL) return 0; if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) return 0; key = PyTuple_GET_ITEM(obj, 0); value = PyTuple_GET_ITEM(obj, 1); found = PyDict_GetItemWithError((PyObject *)dv->dv_dict, key); if (found == NULL) { if (PyErr_Occurred()) return -1; return 0; } Py_INCREF(found); result = PyObject_RichCompareBool(value, found, Py_EQ); Py_DECREF(found); return result; } static PySequenceMethods dictitems_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictitems_contains, /* sq_contains */ }; static PyObject* dictitems_reversed(_PyDictViewObject *dv); PyDoc_STRVAR(reversed_items_doc, "Return a reverse iterator over the dict items."); static PyMethodDef dictitems_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {"__reversed__", (PyCFunction)(void(*)(void))dictitems_reversed, METH_NOARGS, reversed_items_doc}, {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictItems_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_items", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictitems_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dictitems_iter, /* tp_iter */ 0, /* tp_iternext */ dictitems_methods, /* tp_methods */ 0, }; static PyObject * dictitems_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _PyDictView_New(dict, &PyDictItems_Type); } static PyObject * dictitems_reversed(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterItem_Type); } /*** dict_values ***/ static PyObject * dictvalues_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictIterValue_Type); } static PySequenceMethods dictvalues_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)0, /* sq_contains */ }; static PyObject* dictvalues_reversed(_PyDictViewObject *dv); PyDoc_STRVAR(reversed_values_doc, "Return a reverse iterator over the dict values."); static PyMethodDef dictvalues_methods[] = { {"__reversed__", (PyCFunction)(void(*)(void))dictvalues_reversed, METH_NOARGS, reversed_values_doc}, {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictValues_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_values", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ 0, /* tp_as_number */ &dictvalues_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dictvalues_iter, /* tp_iter */ 0, /* tp_iternext */ dictvalues_methods, /* tp_methods */ 0, }; static PyObject * dictvalues_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _PyDictView_New(dict, &PyDictValues_Type); } static PyObject * dictvalues_reversed(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterValue_Type); } /* Returns NULL if cannot allocate a new PyDictKeysObject, but does not set an error */ PyDictKeysObject * _PyDict_NewKeysForClass(void) { PyDictKeysObject *keys = new_keys_object(PyDict_MINSIZE); if (keys == NULL) PyErr_Clear(); else keys->dk_lookup = lookdict_split; return keys; } #define CACHED_KEYS(tp) (((PyHeapTypeObject*)tp)->ht_cached_keys) PyObject * PyObject_GenericGetDict(PyObject *obj, void *context) { PyObject *dict, **dictptr = _PyObject_GetDictPtr(obj); if (dictptr == NULL) { PyErr_SetString(PyExc_AttributeError, "This object has no __dict__"); return NULL; } dict = *dictptr; if (dict == NULL) { PyTypeObject *tp = Py_TYPE(obj); if ((tp->tp_flags & Py_TPFLAGS_HEAPTYPE) && CACHED_KEYS(tp)) { dictkeys_incref(CACHED_KEYS(tp)); *dictptr = dict = new_dict_with_shared_keys(CACHED_KEYS(tp)); } else { *dictptr = dict = PyDict_New(); } } Py_XINCREF(dict); return dict; } int _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, PyObject *key, PyObject *value) { PyObject *dict; int res; PyDictKeysObject *cached; assert(dictptr != NULL); if ((tp->tp_flags & Py_TPFLAGS_HEAPTYPE) && (cached = CACHED_KEYS(tp))) { assert(dictptr != NULL); dict = *dictptr; if (dict == NULL) { dictkeys_incref(cached); dict = new_dict_with_shared_keys(cached); if (dict == NULL) return -1; *dictptr = dict; } if (value == NULL) { res = PyDict_DelItem(dict, key); // Since key sharing dict doesn't allow deletion, PyDict_DelItem() // always converts dict to combined form. if ((cached = CACHED_KEYS(tp)) != NULL) { CACHED_KEYS(tp) = NULL; dictkeys_decref(cached); } } else { int was_shared = (cached == ((PyDictObject *)dict)->ma_keys); res = PyDict_SetItem(dict, key, value); if (was_shared && (cached = CACHED_KEYS(tp)) != NULL && cached != ((PyDictObject *)dict)->ma_keys) { /* PyDict_SetItem() may call dictresize and convert split table * into combined table. In such case, convert it to split * table again and update type's shared key only when this is * the only dict sharing key with the type. * * This is to allow using shared key in class like this: * * class C: * def __init__(self): * # one dict resize happens * self.a, self.b, self.c = 1, 2, 3 * self.d, self.e, self.f = 4, 5, 6 * a = C() */ if (cached->dk_refcnt == 1) { CACHED_KEYS(tp) = make_keys_shared(dict); } else { CACHED_KEYS(tp) = NULL; } dictkeys_decref(cached); if (CACHED_KEYS(tp) == NULL && PyErr_Occurred()) return -1; } } } else { dict = *dictptr; if (dict == NULL) { dict = PyDict_New(); if (dict == NULL) return -1; *dictptr = dict; } if (value == NULL) { res = PyDict_DelItem(dict, key); } else { res = PyDict_SetItem(dict, key, value); } } return res; } void _PyDictKeys_DecRef(PyDictKeysObject *keys) { dictkeys_decref(keys); } python-frozendict/src/frozendict/c_src/3_8/cpython_src/Objects/stringlib/000077500000000000000000000000001470250572200271115ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_8/cpython_src/Objects/stringlib/eq.h000066400000000000000000000015221470250572200276670ustar00rootroot00000000000000/* Fast unicode equal function optimized for dictobject.c and setobject.c */ /* Return 1 if two unicode objects are equal, 0 if not. * unicode_eq() is called when the hash of two unicode objects is equal. */ Py_LOCAL_INLINE(int) unicode_eq(PyObject *aa, PyObject *bb) { assert(PyUnicode_Check(aa)); assert(PyUnicode_Check(bb)); assert(! PyUnicode_READY(aa)); assert(! PyUnicode_READY(bb)); PyUnicodeObject *a = (PyUnicodeObject *)aa; PyUnicodeObject *b = (PyUnicodeObject *)bb; if (PyUnicode_GET_LENGTH(a) != PyUnicode_GET_LENGTH(b)) return 0; if (PyUnicode_GET_LENGTH(a) == 0) return 1; if (PyUnicode_KIND(a) != PyUnicode_KIND(b)) return 0; return memcmp(PyUnicode_1BYTE_DATA(a), PyUnicode_1BYTE_DATA(b), PyUnicode_GET_LENGTH(a) * PyUnicode_KIND(a)) == 0; } python-frozendict/src/frozendict/c_src/3_8/cpython_src/other.c000066400000000000000000000013021470250572200250040ustar00rootroot00000000000000static const unsigned int BitLengthTable[32] = { 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }; unsigned int _Py_bit_length(unsigned long d) { unsigned int d_bits = 0; while (d >= 32) { d_bits += 6; d >>= 6; } d_bits += BitLengthTable[d]; return d_bits; } #define _Py_AS_GC(o) ((PyGC_Head *)(o)-1) #define _PyObject_GC_IS_TRACKED(o) (_Py_AS_GC(o)->_gc_next != 0) #define _PyObject_GC_MAY_BE_TRACKED(obj) \ (PyObject_IS_GC(obj) && \ (!PyTuple_CheckExact(obj) || _PyObject_GC_IS_TRACKED(obj))) #define Py_IS_TYPE(op, type) (Py_TYPE(op) == type) #define PySet_CheckExact(op) Py_IS_TYPE(op, &PySet_Type) python-frozendict/src/frozendict/c_src/3_8/frozendictobject.c000066400000000000000000001660021470250572200246770ustar00rootroot00000000000000#include #include "frozendictobject.h" static PyObject* frozendict_iter(PyDictObject *dict); static int frozendict_equal(PyDictObject* a, PyDictObject* b); #include "other.c" #include "dictobject.c" static void frozendict_free_keys_object(PyDictKeysObject *keys, const int decref_items) { if (decref_items) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n; for (i = 0, n = keys->dk_nentries; i < n; i++) { Py_XDECREF(entries[i].me_key); Py_XDECREF(entries[i].me_value); } } #if PyDict_MAXFREELIST > 0 if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) { keys_free_list[numfreekeys++] = keys; return; } #endif PyObject_Free(keys); } static inline void frozendict_keys_decref(PyDictKeysObject *dk, const int decref_items) { assert(dk->dk_refcnt > 0); #ifdef Py_REF_DEBUG _Py_RefTotal--; #endif if (--dk->dk_refcnt == 0) { frozendict_free_keys_object(dk, decref_items); } } static int frozendict_resize(PyDictObject* mp, Py_ssize_t minsize) { const Py_ssize_t newsize = calculate_keysize(minsize); if (newsize <= 0) { PyErr_NoMemory(); return -1; } assert(IS_POWER_OF_2(newsize)); assert(newsize >= PyDict_MINSIZE); PyDictKeysObject* oldkeys = mp->ma_keys; /* Allocate a new table. */ PyDictKeysObject* new_keys = new_keys_object(newsize); if (new_keys == NULL) { return -1; } // New table must be large enough. assert(new_keys->dk_usable >= mp->ma_used); new_keys->dk_lookup = oldkeys->dk_lookup; const Py_ssize_t numentries = mp->ma_used; PyDictKeyEntry* newentries = DK_ENTRIES(new_keys); memcpy( newentries, DK_ENTRIES(oldkeys), numentries * sizeof(PyDictKeyEntry) ); build_indices(new_keys, newentries, numentries); new_keys->dk_usable -= numentries; new_keys->dk_nentries = numentries; // do not decref the keys inside! frozendict_keys_decref(oldkeys, 0); mp->ma_keys = new_keys; return 0; } static int frozendict_insert(PyDictObject *mp, PyObject *key, const Py_hash_t hash, PyObject *value, int empty) { Py_ssize_t ix; PyObject *old_value; PyDictKeysObject* keys = mp->ma_keys; Py_INCREF(key); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); if (! empty) { ix = keys->dk_lookup(mp, key, hash, &old_value); if (ix == DKIX_ERROR) { Py_DECREF(value); Py_DECREF(key); return -1; } empty = (ix == DKIX_EMPTY); } if (empty) { /* Insert into new slot. */ if (mp->ma_keys->dk_usable <= 0) { /* Need to resize. */ if (frozendict_resize(mp, GROWTH_RATE(mp))) { Py_DECREF(value); Py_DECREF(key); return -1; } // resize changes keys keys = mp->ma_keys; } const Py_ssize_t hashpos = find_empty_slot(keys, hash); const Py_ssize_t dk_nentries = keys->dk_nentries; PyDictKeyEntry* ep = &DK_ENTRIES(keys)[dk_nentries]; dictkeys_set_index(keys, hashpos, dk_nentries); ep->me_key = key; ep->me_hash = hash; ep->me_value = value; mp->ma_used++; keys->dk_usable--; keys->dk_nentries++; assert(keys->dk_usable >= 0); } else { DK_ENTRIES(mp->ma_keys)[ix].me_value = value; Py_DECREF(old_value); /* which **CAN** re-enter (see issue #22653) */ Py_DECREF(key); } ASSERT_CONSISTENT(mp); return 0; } static int frozendict_setitem(PyObject *op, PyObject *key, PyObject *value, int empty) { Py_hash_t hash; assert(key); assert(value); if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { return -1; } } return frozendict_insert((PyDictObject*) op, key, hash, value, empty); } // int _PyFrozendict_SetItem(PyObject *op, // PyObject *key, // PyObject *value, // int empty) { // if (! PyAnyFrozenDict_Check(op)) { // PyErr_BadInternalCall(); // return -1; // } // return frozendict_setitem(op, key, value, empty); // } static PyObject* _frozendict_new( PyTypeObject* type, PyObject* args, PyObject* kwds, const int use_empty_frozendict ); static PyObject * frozendict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value) { PyObject *it; /* iter(iterable) */ PyObject *key; PyObject *d; int status; d = _frozendict_new(&PyFrozenDict_Type, NULL, NULL, 0); if (d == NULL) return NULL; Py_ssize_t size; PyDictObject *mp = (PyDictObject *)d; mp->ma_keys = new_keys_object(PyDict_MINSIZE); if (PyAnyDict_CheckExact(iterable)) { PyObject *oldvalue; Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; size = PyDict_GET_SIZE(iterable); if (mp->ma_keys->dk_usable < size) { if (frozendict_resize(mp, estimate_keysize(size))) { Py_DECREF(d); return NULL; } } while (_d_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) { if (frozendict_insert(mp, key, hash, value, 0)) { Py_DECREF(d); return NULL; } } return d; } else if (PyAnySet_CheckExact(iterable)) { Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; size = PySet_GET_SIZE(iterable); if (mp->ma_keys->dk_usable < size) { if (frozendict_resize(mp, estimate_keysize(size))) { Py_DECREF(d); return NULL; } } while (_PySet_NextEntry(iterable, &pos, &key, &hash)) { if (frozendict_insert(mp, key, hash, value, 0)) { Py_DECREF(d); return NULL; } } } else { it = PyObject_GetIter(iterable); if (it == NULL){ Py_DECREF(d); return NULL; } while ((key = PyIter_Next(it)) != NULL) { status = frozendict_setitem(d, key, value, 0); Py_DECREF(key); if (status < 0) { Py_DECREF(it); Py_DECREF(d); return NULL; } } Py_DECREF(it); if (PyErr_Occurred()) { Py_DECREF(d); return NULL; } } ASSERT_CONSISTENT(mp); if (type == &PyFrozenDict_Type) { return d; } PyObject* args = PyTuple_New(1); if (args == NULL) { Py_DECREF(d); return NULL; } PyTuple_SET_ITEM(args, 0, d); PyObject* res = PyObject_Call((PyObject*) type, args, NULL); Py_DECREF(args); return res; } /* Methods */ #define REPR_GENERIC_START "(" #define REPR_GENERIC_END ")" #define REPR_GENERIC_START_LEN 1 #define REPR_GENERIC_END_LEN 1 static PyObject* frozendict_repr(PyFrozenDictObject* mp) { PyObject* dict_repr_res = dict_repr((PyDictObject*) mp); if (dict_repr_res == NULL) { return NULL; } _PyUnicodeWriter writer; _PyUnicodeWriter_Init(&writer); int error = 0; PyObject* o = (PyObject*) mp; Py_ReprEnter(o); PyTypeObject* type = Py_TYPE(mp); size_t frozendict_name_len = strlen(type->tp_name); writer.min_length = ( frozendict_name_len + REPR_GENERIC_START_LEN + PyObject_Length(dict_repr_res) + REPR_GENERIC_END_LEN ); if (_PyUnicodeWriter_WriteASCIIString( &writer, type->tp_name, frozendict_name_len )) { error = 1; } else { if (_PyUnicodeWriter_WriteASCIIString( &writer, REPR_GENERIC_START, REPR_GENERIC_START_LEN )) { error = 1; } else { if (_PyUnicodeWriter_WriteStr(&writer, dict_repr_res)) { error = 1; } else { error = _PyUnicodeWriter_WriteASCIIString( &writer, REPR_GENERIC_END, REPR_GENERIC_END_LEN ); } } } Py_ReprLeave(o); Py_DECREF(dict_repr_res); if (error) { _PyUnicodeWriter_Dealloc(&writer); return NULL; } return _PyUnicodeWriter_Finish(&writer); } static PyMappingMethods frozendict_as_mapping = { (lenfunc)dict_length, /*mp_length*/ (binaryfunc)dict_subscript, /*mp_subscript*/ }; static int frozendict_merge(PyObject* a, PyObject* b, int empty) { /* We accept for the argument either a concrete dictionary object, * or an abstract "mapping" object. For the former, we can do * things quite efficiently. For the latter, we only require that * PyMapping_Keys() and PyObject_GetItem() be supported. */ assert(a != NULL); assert(PyAnyFrozenDict_Check(a)); assert(b != NULL); PyDictObject* mp = (PyDictObject*) a; if ( PyAnyDict_Check(b) && ( Py_TYPE(b)->tp_iter == PyDict_Type.tp_iter || Py_TYPE(b)->tp_iter == (getiterfunc)frozendict_iter ) ) { PyDictObject* other = (PyDictObject*)b; const Py_ssize_t numentries = other->ma_used; if (other == mp || numentries == 0) { /* a.update(a) or a.update({}); nothing to do */ return 0; } const int is_other_combined = other->ma_values == NULL; PyDictKeysObject* okeys = other->ma_keys; if ( empty && is_other_combined && numentries == okeys->dk_nentries ) { PyDictKeysObject *keys = clone_combined_dict_keys(other); if (keys == NULL) { return -1; } mp->ma_keys = keys; mp->ma_used = numentries; mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(mp); if (_PyObject_GC_IS_TRACKED(other) && !_PyObject_GC_IS_TRACKED(mp)) { PyObject_GC_Track(mp); } return 0; } PyDictKeyEntry* ep0 = DK_ENTRIES(okeys); PyDictKeyEntry* entry; PyObject* key; PyObject* value; Py_hash_t hash; int err; if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } /* Do one big resize at the start, rather than * incrementally resizing as we insert new items. Expect * that there will be no (or few) overlapping keys. */ if (mp->ma_keys->dk_usable < numentries) { if (frozendict_resize(mp, estimate_keysize(mp->ma_used + numentries))) { return -1; } } for (Py_ssize_t i = 0, n = okeys->dk_nentries; i < n; i++) { entry = &ep0[i]; key = entry->me_key; hash = entry->me_hash; if (is_other_combined) { value = entry->me_value; } else { value = other->ma_values[i]; } if (value != NULL) { Py_INCREF(key); Py_INCREF(value); err = frozendict_insert(mp, key, hash, value, empty); Py_DECREF(value); Py_DECREF(key); if (err != 0) { return -1; } if (n != other->ma_keys->dk_nentries) { PyErr_SetString(PyExc_RuntimeError, "dict mutated during update"); return -1; } } } } else { /* Do it the generic, slower way */ PyObject *keys = PyMapping_Keys(b); PyObject *iter; PyObject *key, *value; int status; if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } if (keys == NULL) /* Docstring says this is equivalent to E.keys() so * if E doesn't have a .keys() method we want * AttributeError to percolate up. Might as well * do the same for any other error. */ return -1; iter = PyObject_GetIter(keys); Py_DECREF(keys); if (iter == NULL) return -1; for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) { value = PyObject_GetItem(b, key); if (value == NULL) { Py_DECREF(iter); Py_DECREF(key); return -1; } status = frozendict_setitem(a, key, value, 0); Py_DECREF(key); Py_DECREF(value); if (status < 0) { Py_DECREF(iter); return -1; } } Py_DECREF(iter); if (PyErr_Occurred()) /* Iterator completed, via error */ return -1; } ASSERT_CONSISTENT(a); return 0; } static int frozendict_merge_from_seq2(PyObject* d, PyObject* seq2) { assert(d != NULL); assert(PyAnyFrozenDict_Check(d)); assert(seq2 != NULL); PyObject* it = PyObject_GetIter(seq2); if (it == NULL) { return -1; } PyObject* fast; /* item as a 2-tuple or 2-list */ PyObject* key = NULL; PyObject* value = NULL; Py_ssize_t n; PyObject* item; int res = 0; PyDictObject* mp = (PyDictObject*) d; if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } for (Py_ssize_t i = 0; ; ++i) { fast = NULL; item = PyIter_Next(it); if (item == NULL) { if (PyErr_Occurred()) { res = -1; } break; } /* Convert item to sequence, and verify length 2. */ fast = PySequence_Fast(item, ""); if (fast == NULL) { if (PyErr_ExceptionMatches(PyExc_TypeError)) { PyErr_Format(PyExc_TypeError, "cannot convert dictionary update " "sequence element #%zd to a sequence", i); } Py_DECREF(item); res = -1; break; } n = PySequence_Fast_GET_SIZE(fast); if (n != 2) { PyErr_Format(PyExc_ValueError, "dictionary update sequence element #%zd " "has length %zd; 2 is required", i, n); Py_DECREF(fast); Py_DECREF(item); res = -1; break; } /* Update/merge with this (key, value) pair. */ key = PySequence_Fast_GET_ITEM(fast, 0); Py_INCREF(key); value = PySequence_Fast_GET_ITEM(fast, 1); Py_INCREF(value); if (frozendict_setitem(d, key, value, 0) < 0) { Py_DECREF(key); Py_DECREF(value); Py_DECREF(fast); Py_DECREF(item); res = -1; break; } Py_DECREF(key); Py_DECREF(value); Py_DECREF(fast); Py_DECREF(item); } Py_DECREF(it); ASSERT_CONSISTENT(d); return res; } static int frozendict_update_arg(PyObject *self, PyObject *arg, const int empty) { if (PyAnyDict_CheckExact(arg)) { return frozendict_merge(self, arg, empty); } _Py_IDENTIFIER(keys); PyObject *func; if (_PyObject_LookupAttrId(arg, &PyId_keys, &func) < 0) { return -1; } if (func != NULL) { Py_DECREF(func); return frozendict_merge(self, arg, empty); } return frozendict_merge_from_seq2(self, arg); } static int frozendict_update_common(PyObject* self, PyObject* arg, PyObject* kwds) { int result = 0; const int no_arg = (arg == NULL); if (! no_arg) { result = frozendict_update_arg(self, arg, 1); } if (result == 0 && kwds != NULL) { if (PyArg_ValidateKeywordArguments(kwds)) { result = frozendict_merge(self, kwds, no_arg); } else { result = -1; } } return result; } /* Forward */ static PyObject *frozendictkeys_new(PyObject *, PyObject *); static PyObject *frozendictitems_new(PyObject *, PyObject *); static PyObject *frozendictvalues_new(PyObject *, PyObject *); #define MINUSONE_HASH ((Py_hash_t) -1) static Py_hash_t frozendict_hash(PyObject* self) { PyFrozenDictObject* frozen_self = (PyFrozenDictObject*) self; Py_hash_t hash; if (frozen_self->ma_hash != MINUSONE_HASH) { hash = frozen_self->ma_hash; } else { PyObject* frozen_items_tmp = frozendictitems_new(self, NULL); int save_hash = 1; if (frozen_items_tmp == NULL) { hash = MINUSONE_HASH; save_hash = 0; } else { PyObject* frozen_items = PyFrozenSet_New(frozen_items_tmp); Py_DECREF(frozen_items_tmp); if (frozen_items == NULL) { hash = MINUSONE_HASH; save_hash = 0; } else { hash = PyFrozenSet_Type.tp_hash(frozen_items); Py_DECREF(frozen_items); } } if (save_hash) { frozen_self->ma_hash = hash; } } return hash; } static PyObject* frozendict_copy(PyObject* o, PyObject* Py_UNUSED(ignored)) { if (PyAnyFrozenDict_CheckExact(o)) { Py_INCREF(o); return o; } PyObject* args = PyTuple_New(1); if (args == NULL) { return NULL; } Py_INCREF(o); PyTuple_SET_ITEM(args, 0, o); PyTypeObject* type = Py_TYPE(o); PyObject* res = PyObject_Call((PyObject *) type, args, NULL); Py_DECREF(args); return res; } PyObject* frozendict_deepcopy(PyObject* self, PyObject* memo) { if (PyAnyFrozenDict_CheckExact(self)) { frozendict_hash(self); if (PyErr_Occurred()) { PyErr_Clear(); } else { Py_INCREF(self); return self; } } if (! PyAnyFrozenDict_Check(self)) { Py_RETURN_NOTIMPLEMENTED; } PyObject* d = PyDict_New(); if (d == NULL) { return NULL; } PyObject* copy_module_name = NULL; PyObject* copy_module = NULL; PyObject* deepcopy_fun = NULL; PyObject* deep_args = NULL; PyObject* deep_d = NULL; PyObject* args = NULL; PyObject* res = NULL; int decref_d = 1; int decref_deep_d = 1; if (PyDict_Merge(d, self, 1)) { goto end; } copy_module_name = PyUnicode_FromString("copy"); if (copy_module_name == NULL) { goto end; } copy_module = PyImport_Import(copy_module_name); if (copy_module == NULL) { goto end; } deepcopy_fun = PyObject_GetAttrString(copy_module, "deepcopy"); if (deepcopy_fun == NULL) { goto end; } deep_args = PyTuple_New(2); if (deep_args == NULL) { goto end; } PyTuple_SET_ITEM(deep_args, 0, d); decref_d = 0; Py_INCREF(memo); PyTuple_SET_ITEM(deep_args, 1, memo); deep_d = PyObject_CallObject(deepcopy_fun, deep_args); if (deep_d == NULL) { goto end; } args = PyTuple_New(1); if (args == NULL) { goto end; } PyTuple_SET_ITEM(args, 0, deep_d); decref_deep_d = 0; PyTypeObject* type = Py_TYPE(self); res = PyObject_Call((PyObject *) type, args, NULL); end: Py_XDECREF(args); Py_XDECREF(deep_args); Py_XDECREF(deepcopy_fun); Py_XDECREF(copy_module); Py_XDECREF(copy_module_name); if (decref_d) { Py_DECREF(d); } if (decref_deep_d) { Py_DECREF(deep_d); } return res; } static int frozendict_equal(PyDictObject* a, PyDictObject* b) { if (a == b) { return 1; } if (a->ma_used != b->ma_used) { /* can't be equal if # of entries differ */ return 0; } PyDictKeysObject* keys = a->ma_keys; PyDictKeyEntry* ep; PyObject* aval; int cmp = 1; PyObject* bval; PyObject* key; /* Same # of entries -- check all of 'em. Exit early on any diff. */ for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) { ep = &DK_ENTRIES(keys)[i]; aval = ep->me_value; Py_INCREF(aval); key = ep->me_key; Py_INCREF(key); /* reuse the known hash value */ b->ma_keys->dk_lookup(b, key, ep->me_hash, &bval); if (bval == NULL) { if (PyErr_Occurred()) { cmp = -1; } else { cmp = 0; } } else { Py_INCREF(bval); cmp = PyObject_RichCompareBool(aval, bval, Py_EQ); Py_DECREF(bval); } Py_DECREF(key); Py_DECREF(aval); if (cmp <= 0) { /* error or not equal */ break; } } return cmp; } static Py_ssize_t dict_get_index(PyDictObject *self, PyObject *key) { Py_hash_t hash; PyObject* val; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return DKIX_ERROR; } return (self->ma_keys->dk_lookup) (self, key, hash, &val); } static PyObject * frozendict_reduce(PyFrozenDictObject* mp, PyObject *Py_UNUSED(ignored)) { PyObject *d = PyDict_New(); if (d == NULL) { return NULL; } if (PyDict_Merge(d, (PyObject *)mp, 1)) { Py_DECREF(d); return NULL; } return Py_BuildValue("O(N)", Py_TYPE(mp), d); } static PyObject* frozendict_clone(PyObject* self) { PyTypeObject* type = Py_TYPE(self); PyObject* new_op = type->tp_alloc(type, 0); if (new_op == NULL){ return NULL; } if (type == &PyFrozenDict_Type) { PyObject_GC_UnTrack(new_op); } PyDictObject* mp = (PyDictObject*) self; PyDictKeysObject *keys = clone_combined_dict_keys(mp); if (keys == NULL) { return NULL; } PyFrozenDictObject* new_mp = (PyFrozenDictObject*) new_op; new_mp->ma_keys = keys; if (_PyObject_GC_IS_TRACKED(mp) && !_PyObject_GC_IS_TRACKED(new_mp)) { PyObject_GC_Track(new_mp); } new_mp->ma_used = mp->ma_used; new_mp->ma_hash = MINUSONE_HASH; new_mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(new_mp); return new_op; } static PyObject* frozendict_set( PyObject* self, PyObject* const* args, Py_ssize_t nargs ) { if (!_PyArg_CheckPositional("set", nargs, 2, 2)) { return NULL; } PyObject* new_op = frozendict_clone(self); if (new_op == NULL) { return NULL; } PyObject* set_key = args[0]; if (frozendict_setitem(new_op, set_key, args[1], 0)) { Py_DECREF(new_op); return NULL; } if ( ((PyDictObject*) self)->ma_keys->dk_lookup == lookdict_unicode_nodummy && ! PyUnicode_CheckExact(set_key) ) { ((PyFrozenDictObject*) new_op)->ma_keys->dk_lookup = lookdict; } return new_op; } static PyObject* frozendict_setdefault( PyObject* self, PyObject* const* args, Py_ssize_t nargs ) { if (!_PyArg_CheckPositional("setdefault", nargs, 1, 2)) { return NULL; } PyObject* set_key = args[0]; if (PyDict_Contains(self, set_key)) { Py_INCREF(self); return self; } PyObject* new_op = frozendict_clone(self); if (new_op == NULL) { return NULL; } PyObject* val; if (nargs == 2) { val = args[1]; } else { val = Py_None; } if (frozendict_setitem(new_op, set_key, val, 0)) { Py_DECREF(new_op); return NULL; } if ( ((PyDictObject*) self)->ma_keys->dk_lookup == lookdict_unicode_nodummy && ! PyUnicode_CheckExact(set_key) ) { ((PyFrozenDictObject*) new_op)->ma_keys->dk_lookup = lookdict; } return new_op; } static PyObject* frozendict_delete( PyObject* self, PyObject *const *args, Py_ssize_t nargs ) { if (! _PyArg_CheckPositional("delete", nargs, 1, 1)) { return NULL; } PyDictObject* mp = (PyDictObject*) self; PyObject* del_key = args[0]; const Py_ssize_t ix = dict_get_index(mp, del_key); if (ix == DKIX_ERROR) { return NULL; } if (ix == DKIX_EMPTY) { _PyErr_SetKeyError(del_key); return NULL; } const Py_ssize_t size = mp->ma_used; const Py_ssize_t sizemm = size - 1; if (sizemm == 0) { PyObject* args = PyTuple_New(0); if (args == NULL) { return NULL; } return PyObject_Call((PyObject*) Py_TYPE(self), args, NULL); } PyTypeObject* type = Py_TYPE(self); PyObject* new_op = type->tp_alloc(type, 0); if (new_op == NULL) { return NULL; } if (type == &PyFrozenDict_Type) { PyObject_GC_UnTrack(new_op); } const Py_ssize_t newsize = estimate_keysize(sizemm); if (newsize <= 0) { Py_DECREF(new_op); PyErr_NoMemory(); return NULL; } assert(IS_POWER_OF_2(newsize)); assert(newsize >= PyDict_MINSIZE); /* Allocate a new table. */ PyDictKeysObject* new_keys = new_keys_object(newsize); if (new_keys == NULL) { Py_DECREF(new_op); return NULL; } const PyDictKeysObject* old_keys = mp->ma_keys; new_keys->dk_lookup = old_keys->dk_lookup; PyFrozenDictObject* new_mp = (PyFrozenDictObject*) new_op; // New table must be large enough. assert(new_keys->dk_usable >= new_mp->ma_used); new_mp->ma_keys = new_keys; new_mp->ma_hash = MINUSONE_HASH; new_mp->ma_version_tag = DICT_NEXT_VERSION(); PyObject* key; PyObject* value; Py_hash_t hash; Py_ssize_t hashpos; PyDictKeyEntry* old_entries = DK_ENTRIES(old_keys); PyDictKeyEntry* new_entries = DK_ENTRIES(new_keys); PyDictKeyEntry* old_entry; PyDictKeyEntry* new_entry; Py_ssize_t new_i; int deleted = 0; for (Py_ssize_t i = 0; i < size; i++) { if (i == ix) { deleted = 1; continue; } new_i = i - deleted; old_entry = &old_entries[i]; hash = old_entry->me_hash; key = old_entry->me_key; value = old_entry->me_value; Py_INCREF(key); Py_INCREF(value); hashpos = find_empty_slot(new_keys, hash); dictkeys_set_index(new_keys, hashpos, new_i); new_entry = &new_entries[new_i]; new_entry->me_key = key; new_entry->me_hash = hash; new_entry->me_value = value; } new_mp->ma_used = sizemm; new_keys->dk_usable -= sizemm; new_keys->dk_nentries = sizemm; ASSERT_CONSISTENT(new_mp); return new_op; } static const PyObject* frozendict_key( PyObject* self, PyObject *const *args, Py_ssize_t nargs ) { if (! _PyArg_CheckPositional("key", nargs, 0, 1)) { return NULL; } Py_ssize_t index; Py_ssize_t passed_index; PyDictObject* d = (PyDictObject*) self; const Py_ssize_t size = d->ma_used; if (nargs > 0) { index = PyLong_AsSsize_t(args[0]); passed_index = index; if (index < 0) { if (PyErr_Occurred()) { return NULL; } index += size; } } else { index = 0; passed_index = index; } const Py_ssize_t maxindex = size - 1; if (index > maxindex || index < 0) { PyErr_Format( PyExc_IndexError, "%s index %zd out of range %zd", Py_TYPE(self)->tp_name, passed_index, maxindex ); return NULL; } const PyObject* res = DK_ENTRIES(d->ma_keys)[index].me_key; Py_INCREF(res); return res; } static const PyObject* frozendict_value( PyObject* self, PyObject *const *args, Py_ssize_t nargs ) { if (! _PyArg_CheckPositional("value", nargs, 0, 1)) { return NULL; } Py_ssize_t index; Py_ssize_t passed_index; PyDictObject* d = (PyDictObject*) self; const Py_ssize_t size = d->ma_used; if (nargs > 0) { index = PyLong_AsSsize_t(args[0]); passed_index = index; if (index < 0) { if (PyErr_Occurred()) { return NULL; } index += size; } } else { index = 0; passed_index = index; } const Py_ssize_t maxindex = size - 1; if (index > maxindex || index < 0) { PyErr_Format( PyExc_IndexError, "%s index %zd out of range %zd", Py_TYPE(self)->tp_name, passed_index, maxindex ); return NULL; } const PyObject* res = DK_ENTRIES(d->ma_keys)[index].me_value; Py_INCREF(res); return res; } static const PyObject* frozendict_item( PyObject* self, PyObject *const *args, Py_ssize_t nargs ) { if (! _PyArg_CheckPositional("item", nargs, 0, 1)) { return NULL; } Py_ssize_t index; Py_ssize_t passed_index; PyDictObject* d = (PyDictObject*) self; const Py_ssize_t size = d->ma_used; if (nargs > 0) { index = PyLong_AsSsize_t(args[0]); passed_index = index; if (index < 0) { if (PyErr_Occurred()) { return NULL; } index += size; } } else { index = 0; passed_index = index; } const Py_ssize_t maxindex = size - 1; if (index > maxindex || index < 0) { PyErr_Format( PyExc_IndexError, "%s index %zd out of range %zd", Py_TYPE(self)->tp_name, passed_index, maxindex ); return NULL; } PyObject* key = DK_ENTRIES(d->ma_keys)[index].me_key; Py_INCREF(key); PyObject* val = DK_ENTRIES(d->ma_keys)[index].me_value; Py_INCREF(val); const PyObject* res = PyTuple_New(2); PyTuple_SET_ITEM(res, 0, key); PyTuple_SET_ITEM(res, 1, val); return res; } PyDoc_STRVAR(frozendict_set_doc, "set($self, key, value, /)\n" "--\n" "\n" "Returns a copy of the dictionary with the new (key, value) item. "); PyDoc_STRVAR(frozendict_setdefault_doc, "set($self, key[, default], /)\n" "--\n" "\n" "If key is in the dictionary, it returns the dictionary unchanged. \n" "Otherwise, it returns a copy of the dictionary with the new (key, default) item; \n" "default argument is optional and is None by default. "); PyDoc_STRVAR(frozendict_delete_doc, "delete($self, key, /)\n" "--\n" "\n" "Returns a copy of the dictionary without the item of the corresponding key. "); PyDoc_STRVAR(frozendict_key_doc, "key($self[, index], /)\n" "--\n" "\n" "Get the key at the specified index (insertion order). If index is not \n" "passed, it defaults to 0. If index is negative, returns the key at \n" "position size + index. "); PyDoc_STRVAR(frozendict_value_doc, "value($self[, index], /)\n" "--\n" "\n" "Get the value at the specified index (insertion order). If index is not \n" "passed, it defaults to 0. If index is negative, returns the value at \n" "position size + index. "); PyDoc_STRVAR(frozendict_item_doc, "item($self[, index], /)\n" "--\n" "\n" "Get the (key, value) item at the specified index (insertion order). If \n" "index is not passed, it defaults to 0. If index is negative, returns the \n" "item at position size + index. "); static PyMethodDef frozendict_mapp_methods[] = { DICT___CONTAINS___METHODDEF {"__getitem__", (PyCFunction)(void(*)(void))dict_subscript, METH_O | METH_COEXIST, getitem__doc__}, {"__sizeof__", (PyCFunction)(void(*)(void))dict_sizeof, METH_NOARGS, sizeof__doc__}, DICT_GET_METHODDEF {"keys", frozendictkeys_new, METH_NOARGS, keys__doc__}, {"items", frozendictitems_new, METH_NOARGS, items__doc__}, {"values", frozendictvalues_new, METH_NOARGS, values__doc__}, {"fromkeys", (PyCFunction)(void(*)(void))dict_fromkeys, METH_FASTCALL|METH_CLASS, dict_fromkeys__doc__}, {"copy", (PyCFunction)frozendict_copy, METH_NOARGS, copy__doc__}, {"__copy__", (PyCFunction)frozendict_copy, METH_NOARGS, "Returns a copy of the object."}, {"__deepcopy__", (PyCFunction)frozendict_deepcopy, METH_O, "Returns a deepcopy of the object."}, DICT___REVERSED___METHODDEF {"__reduce__", (PyCFunction)(void(*)(void))frozendict_reduce, METH_NOARGS, ""}, {"set", (PyCFunction)(void(*)(void)) frozendict_set, METH_FASTCALL, frozendict_set_doc}, {"setdefault", (PyCFunction)(void(*)(void)) frozendict_setdefault, METH_FASTCALL, frozendict_setdefault_doc}, {"delete", (PyCFunction)(void(*)(void)) frozendict_delete, METH_FASTCALL, frozendict_delete_doc}, {"key", (PyCFunction)(void(*)(void)) frozendict_key, METH_FASTCALL, frozendict_key_doc}, {"value", (PyCFunction)(void(*)(void)) frozendict_value, METH_FASTCALL, frozendict_value_doc}, {"item", (PyCFunction)(void(*)(void)) frozendict_item, METH_FASTCALL, frozendict_item_doc}, {NULL, NULL} /* sentinel */ }; static PyObject* frozendict_new_barebone(PyTypeObject* type) { PyObject* self = type->tp_alloc(type, 0); if (self == NULL) { return NULL; } /* The object has been implicitly tracked by tp_alloc */ if (type == &PyFrozenDict_Type) { PyObject_GC_UnTrack(self); } PyFrozenDictObject* mp = (PyFrozenDictObject*) self; mp->ma_keys = NULL; mp->ma_values = NULL; mp->ma_used = 0; mp->ma_hash = MINUSONE_HASH; return self; } // empty frozendict singleton static PyObject* empty_frozendict = NULL; // if frozendict is empty, return the empty singleton static PyObject* frozendict_create_empty( PyFrozenDictObject* mp, const PyTypeObject* type, const int use_empty_frozendict ) { if (mp->ma_used == 0) { if (use_empty_frozendict && type == &PyFrozenDict_Type) { if (empty_frozendict == NULL) { empty_frozendict = (PyObject*) mp; Py_INCREF(Py_EMPTY_KEYS); ((PyDictObject*) empty_frozendict)->ma_keys = Py_EMPTY_KEYS; mp->ma_version_tag = DICT_NEXT_VERSION(); } else { Py_DECREF(mp); } Py_INCREF(empty_frozendict); return empty_frozendict; } else { if (mp->ma_keys != NULL) { frozendict_keys_decref(mp->ma_keys, 0); } Py_INCREF(Py_EMPTY_KEYS); mp->ma_keys = Py_EMPTY_KEYS; return NULL; } } return NULL; } static PyObject* frozendict_vectorcall(PyObject* type, PyObject* const* args, size_t nargsf, PyObject* kwnames) { assert(PyType_Check(type)); Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); if (! _PyArg_CheckPositional("dict", nargs, 0, 1)) { return NULL; } PyTypeObject* ttype = (PyTypeObject*) type; Py_ssize_t size; PyObject* arg = NULL; if (nargs == 1) { arg = args[0]; // only argument is a frozendict if ( arg != NULL && PyAnyFrozenDict_CheckExact(arg) && ttype == &PyFrozenDict_Type ) { size = ( kwnames == NULL ? 0 : PyTuple_GET_SIZE(kwnames) ); if (size == 0) { Py_INCREF(arg); return arg; } } } PyObject* self = frozendict_new_barebone(ttype); PyFrozenDictObject* mp = (PyFrozenDictObject*) self; int empty = 1; if (nargs == 1) { empty = 0; if (frozendict_update_arg(self, arg, 1) < 0) { Py_DECREF(self); return NULL; } args++; } if (kwnames != NULL) { if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } size = ( kwnames == NULL ? 0 : PyTuple_GET_SIZE(kwnames) ); if (mp->ma_keys->dk_usable < size) { if (frozendict_resize((PyDictObject*) self, estimate_keysize(mp->ma_used + size))) { return NULL; } } for (Py_ssize_t i = 0; i < size; i++) { if (frozendict_setitem(self, PyTuple_GET_ITEM(kwnames, i), args[i], empty) < 0) { Py_DECREF(self); return NULL; } } } PyObject* self_empty = frozendict_create_empty(mp, ttype, 1); if (self_empty != NULL) { return self_empty; } mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(mp); return self; } static PyObject* _frozendict_new( PyTypeObject* type, PyObject* args, PyObject* kwds, const int use_empty_frozendict ) { assert(type != NULL && type->tp_alloc != NULL); PyObject* arg = NULL; if (args != NULL && ! PyArg_UnpackTuple(args, "dict", 0, 1, &arg)) { return NULL; } const int arg_is_frozendict = (arg != NULL && PyAnyFrozenDict_CheckExact(arg)); const int kwds_size = ((kwds != NULL) ? ((PyDictObject*) kwds)->ma_used : 0 ); // only argument is a frozendict if (arg_is_frozendict && kwds_size == 0 && type == &PyFrozenDict_Type) { Py_INCREF(arg); return arg; } PyObject* self = frozendict_new_barebone(type); PyFrozenDictObject* mp = (PyFrozenDictObject*) self; if (frozendict_update_common(self, arg, kwds)) { Py_DECREF(self); return NULL; } PyObject* empty = frozendict_create_empty(mp, type, use_empty_frozendict); if (empty != NULL) { return empty; } mp->ma_version_tag = DICT_NEXT_VERSION(); return self; } static PyObject* frozendict_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { return _frozendict_new(type, args, kwds, 1); } static PyObject* frozendict_or(PyObject *self, PyObject *other) { if (! PyAnyFrozenDict_Check(self) || ! PyAnyDict_Check(other)) { Py_RETURN_NOTIMPLEMENTED; } PyObject* new = frozendict_clone(self); if (new == NULL) { return NULL; } if (frozendict_update_arg(new, other, 0)) { Py_DECREF(new); return NULL; } return new; } static PyNumberMethods frozendict_as_number = { .nb_or = frozendict_or, }; #define FROZENDICT_CLASS_NAME "frozendict" #define FROZENDICT_MODULE_NAME "frozendict" #define FROZENDICT_FULL_NAME FROZENDICT_MODULE_NAME "." FROZENDICT_CLASS_NAME PyDoc_STRVAR(frozendict_doc, "An immutable version of dict.\n" "\n" FROZENDICT_FULL_NAME "() -> returns an empty immutable dictionary\n" FROZENDICT_FULL_NAME "(mapping) -> returns an immutable dictionary initialized from a mapping object's\n" " (key, value) pairs\n" FROZENDICT_FULL_NAME "(iterable) -> returns an immutable dictionary, equivalent to:\n" " d = {}\n" " " " for k, v in iterable:\n" " d[k] = v\n" " " " " FROZENDICT_FULL_NAME "(d)\n" FROZENDICT_FULL_NAME "(**kwargs) -> returns an immutable dictionary initialized with the name=value pairs\n" " in the keyword argument list. For example: " FROZENDICT_FULL_NAME "(one=1, two=2)"); static PyTypeObject PyFrozenDict_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_FULL_NAME, /* tp_name */ sizeof(PyFrozenDictObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)dict_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)frozendict_repr, /* tp_repr */ &frozendict_as_number, /* tp_as_number */ &dict_as_sequence, /* tp_as_sequence */ &frozendict_as_mapping, /* tp_as_mapping */ (hashfunc)frozendict_hash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ frozendict_doc, /* tp_doc */ dict_traverse, /* tp_traverse */ 0, /* tp_clear */ dict_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendict_iter, /* tp_iter */ 0, /* tp_iternext */ frozendict_mapp_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ frozendict_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ .tp_vectorcall = frozendict_vectorcall, }; /* Dictionary iterator types */ static PyObject* frozendict_iter(PyDictObject *dict) { return dictiter_new(dict, &PyFrozenDictIterKey_Type); } static PyObject* frozendictiter_iternextkey(dictiterobject* di) { Py_ssize_t pos = di->di_pos; assert(pos >= 0); PyDictObject* d = di->di_dict; if (d == NULL) { return NULL; } assert(PyAnyFrozenDict_Check(d)); if (pos >= d->ma_used) { di->di_dict = NULL; Py_DECREF(d); return NULL; } PyObject* key = DK_ENTRIES(d->ma_keys)[pos].me_key; assert(key != NULL); di->di_pos++; di->len--; Py_INCREF(key); return key; } static PyTypeObject PyFrozenDictIterKey_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".keyiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)frozendictiter_iternextkey, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject* frozendictiter_iternextvalue(dictiterobject* di) { Py_ssize_t pos = di->di_pos; assert(pos >= 0); PyDictObject* d = di->di_dict; if (d == NULL) { return NULL; } assert(PyAnyFrozenDict_Check(d)); if (pos >= d->ma_used) { di->di_dict = NULL; Py_DECREF(d); return NULL; } PyObject* val = DK_ENTRIES(d->ma_keys)[pos].me_value; assert(val != NULL); di->di_pos++; di->len--; Py_INCREF(val); return val; } static PyTypeObject PyFrozenDictIterValue_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".valueiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)frozendictiter_iternextvalue, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject* frozendictiter_iternextitem(dictiterobject* di) { Py_ssize_t pos = di->di_pos; assert(pos >= 0); PyDictObject* d = di->di_dict; if (d == NULL) { return NULL; } assert(PyAnyFrozenDict_Check(d)); if (pos >= d->ma_used) { di->di_dict = NULL; Py_DECREF(d); return NULL; } PyDictKeyEntry* entry_ptr = &DK_ENTRIES(d->ma_keys)[pos]; PyObject* key = entry_ptr->me_key; PyObject* val = entry_ptr->me_value; assert(key != NULL); assert(val != NULL); di->di_pos++; di->len--; Py_INCREF(key); Py_INCREF(val); PyObject* result; if (Py_REFCNT(di->di_result) == 1) { result = di->di_result; PyObject *oldkey = PyTuple_GET_ITEM(result, 0); PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); if (!_PyObject_GC_IS_TRACKED(result)) { PyObject_GC_Track(result); } } else { result = PyTuple_New(2); if (result == NULL) return NULL; } PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, val); /* steals reference */ return result; } static PyTypeObject PyFrozenDictIterItem_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".itemiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)frozendictiter_iternextitem, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; /*** dict_keys ***/ static PyObject * frozendictkeys_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyFrozenDictIterKey_Type); } static PyTypeObject PyFrozenDictKeys_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".keys", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictkeys_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendictkeys_iter, /* tp_iter */ 0, /* tp_iternext */ dictkeys_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * frozendictkeys_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _d_PyDictView_New(dict, &PyFrozenDictKeys_Type); } /*** dict_items ***/ static PyObject * frozendictitems_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyFrozenDictIterItem_Type); } static PyTypeObject PyFrozenDictItems_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".items", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictitems_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendictitems_iter, /* tp_iter */ 0, /* tp_iternext */ dictitems_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * frozendictitems_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _d_PyDictView_New(dict, &PyFrozenDictItems_Type); } /*** dict_values ***/ static PyObject * frozendictvalues_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyFrozenDictIterValue_Type); } static PyTypeObject PyFrozenDictValues_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".values", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ 0, /* tp_as_number */ &dictvalues_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendictvalues_iter, /* tp_iter */ 0, /* tp_iternext */ dictvalues_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * frozendictvalues_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _d_PyDictView_New(dict, &PyFrozenDictValues_Type); } static int frozendict_exec(PyObject *m) { /* Finalize the type object including setting type of the new type * object; doing it here is required for portability, too. */ if (PyType_Ready(&PyFrozenDict_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictIterKey_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictIterValue_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictIterItem_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictKeys_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictItems_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictValues_Type) < 0) { goto fail; } PyModule_AddObject(m, FROZENDICT_CLASS_NAME, (PyObject *)&PyFrozenDict_Type); return 0; fail: Py_XDECREF(m); return -1; } static struct PyModuleDef_Slot frozendict_slots[] = { {Py_mod_exec, frozendict_exec}, {0, NULL}, }; static struct PyModuleDef frozendictmodule = { PyModuleDef_HEAD_INIT, FROZENDICT_MODULE_NAME, /* name of module */ NULL, /* module documentation, may be NULL */ 0, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ NULL, frozendict_slots, NULL, NULL, NULL }; PyMODINIT_FUNC PyInit__frozendict(void) { return PyModuleDef_Init(&frozendictmodule); } python-frozendict/src/frozendict/c_src/3_9/000077500000000000000000000000001470250572200211715ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_9/Include/000077500000000000000000000000001470250572200225545ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_9/Include/cpython/000077500000000000000000000000001470250572200242405ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_9/Include/cpython/frozendictobject.h000066400000000000000000000004601470250572200277470ustar00rootroot00000000000000#ifndef Py_CPYTHON_FROZENDICTOBJECT_H # error "this header file must not be included directly" #endif typedef struct { PyObject_HEAD Py_ssize_t ma_used; uint64_t ma_version_tag; PyDictKeysObject* ma_keys; PyObject** ma_values; Py_hash_t ma_hash; } PyFrozenDictObject; python-frozendict/src/frozendict/c_src/3_9/Include/frozendictobject.h000066400000000000000000000050501470250572200262630ustar00rootroot00000000000000#ifndef Py_FROZENDICTOBJECT_H #define Py_FROZENDICTOBJECT_H #ifdef __cplusplus extern "C" { #endif // PyAPI_DATA(PyTypeObject) PyFrozenDict_Type; static PyTypeObject PyFrozenDict_Type; // PyAPI_DATA(PyTypeObject) PyCoold_Type; static PyTypeObject PyCoold_Type; #define PyFrozenDict_Check(op) \ ( \ Py_IS_TYPE(op, &PyFrozenDict_Type) \ || PyType_IsSubtype(Py_TYPE(op), &PyFrozenDict_Type) \ ) #define PyFrozenDict_CheckExact(op) Py_IS_TYPE(op, &PyFrozenDict_Type) #define PyAnyFrozenDict_Check(op) \ ( \ Py_IS_TYPE(op, &PyFrozenDict_Type) \ || Py_IS_TYPE(op, &PyCoold_Type) \ || PyType_IsSubtype(Py_TYPE(op), &PyFrozenDict_Type) \ || PyType_IsSubtype(Py_TYPE(op), &PyCoold_Type) \ ) #define PyAnyFrozenDict_CheckExact(op) \ ( \ Py_IS_TYPE(op, &PyFrozenDict_Type) \ || Py_IS_TYPE(op, &PyCoold_Type) \ ) #define PyAnyDict_Check(ob) \ ( \ PyDict_Check(ob) \ || Py_IS_TYPE(ob, &PyFrozenDict_Type) \ || Py_IS_TYPE(ob, &PyCoold_Type) \ || PyType_IsSubtype(Py_TYPE(ob), &PyFrozenDict_Type) \ || PyType_IsSubtype(Py_TYPE(ob), &PyCoold_Type) \ ) #define PyAnyDict_CheckExact(op) ( \ (Py_IS_TYPE(op, &PyDict_Type)) \ || (Py_IS_TYPE(op, &PyFrozenDict_Type)) \ || (Py_IS_TYPE(op, &PyCoold_Type)) \ ) // PyAPI_DATA(PyTypeObject) PyFrozenDictIterKey_Type; static PyTypeObject PyFrozenDictIterKey_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictIterValues_Type; static PyTypeObject PyFrozenDictIterValue_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictIterItem_Type; static PyTypeObject PyFrozenDictIterItem_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictKeys_Type; static PyTypeObject PyFrozenDictKeys_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictValues_Type; static PyTypeObject PyFrozenDictValues_Type; // PyAPI_DATA(PyTypeObject) PyFrozenDictItems_Type; static PyTypeObject PyFrozenDictItems_Type; #define PyAnyDictKeys_Check(op) (PyDictKeys_Check(op) || PyObject_TypeCheck(op, &PyFrozenDictKeys_Type)) #define PyAnyDictValues_Check(op) (PyDictValues_Check(op) || PyObject_TypeCheck(op, &PyFrozenDictValues_Type)) #define PyAnyDictItems_Check(op) (PyDictItems_Check(op) || PyObject_TypeCheck(op, &PyFrozenDictItems_Type)) /* This excludes Values, since they are not sets. */ # define PyAnyDictViewSet_Check(op) \ (PyAnyDictKeys_Check(op) || PyAnyDictItems_Check(op)) #ifndef Py_LIMITED_API # define Py_CPYTHON_FROZENDICTOBJECT_H # include "cpython/frozendictobject.h" # undef Py_CPYTHON_FROZENDICTOBJECT_H #endif #ifdef __cplusplus } #endif #endif python-frozendict/src/frozendict/c_src/3_9/cpython_src/000077500000000000000000000000001470250572200235245ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_9/cpython_src/Objects/000077500000000000000000000000001470250572200251155ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_9/cpython_src/Objects/clinic/000077500000000000000000000000001470250572200263565ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_9/cpython_src/Objects/clinic/dictobject.c.h000066400000000000000000000051111470250572200310600ustar00rootroot00000000000000/*[clinic input] preserve [clinic start generated code]*/ PyDoc_STRVAR(dict_fromkeys__doc__, "fromkeys($type, iterable, value=None, /)\n" "--\n" "\n" "Create a new dictionary with keys from iterable and values set to value."); #define DICT_FROMKEYS_METHODDEF \ {"fromkeys", (PyCFunction)(void(*)(void))dict_fromkeys, METH_FASTCALL|METH_CLASS, dict_fromkeys__doc__}, static PyObject * frozendict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value); static PyObject * dict_fromkeys(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; PyObject *iterable; PyObject *value = Py_None; if (!_PyArg_CheckPositional("fromkeys", nargs, 1, 2)) { goto exit; } iterable = args[0]; if (nargs < 2) { goto skip_optional; } value = args[1]; skip_optional: return_value = frozendict_fromkeys_impl(type, iterable, value); exit: return return_value; } PyDoc_STRVAR(dict___contains____doc__, "__contains__($self, key, /)\n" "--\n" "\n" "True if the dictionary has the specified key, else False."); #define DICT___CONTAINS___METHODDEF \ {"__contains__", (PyCFunction)dict___contains__, METH_O|METH_COEXIST, dict___contains____doc__}, PyDoc_STRVAR(dict_get__doc__, "get($self, key, default=None, /)\n" "--\n" "\n" "Return the value for key if key is in the dictionary, else default."); #define DICT_GET_METHODDEF \ {"get", (PyCFunction)(void(*)(void))dict_get, METH_FASTCALL, dict_get__doc__}, static PyObject * dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value); static PyObject * dict_get(PyDictObject *self, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; PyObject *key; PyObject *default_value = Py_None; if (!_PyArg_CheckPositional("get", nargs, 1, 2)) { goto exit; } key = args[0]; if (nargs < 2) { goto skip_optional; } default_value = args[1]; skip_optional: return_value = dict_get_impl(self, key, default_value); exit: return return_value; } PyDoc_STRVAR(dict___reversed____doc__, "__reversed__($self, /)\n" "--\n" "\n" "Return a reverse iterator over the dict keys."); #define DICT___REVERSED___METHODDEF \ {"__reversed__", (PyCFunction)dict___reversed__, METH_NOARGS, dict___reversed____doc__}, static PyObject * dict___reversed___impl(PyDictObject *self); static PyObject * dict___reversed__(PyDictObject *self, PyObject *Py_UNUSED(ignored)) { return dict___reversed___impl(self); } /*[clinic end generated code: output=7b77c16e43d6735a input=a9049054013a1b77]*/ python-frozendict/src/frozendict/c_src/3_9/cpython_src/Objects/dict-common.h000066400000000000000000000043361470250572200275050ustar00rootroot00000000000000#ifndef Py_DICT_COMMON_H #define Py_DICT_COMMON_H typedef struct { /* Cached hash code of me_key. */ Py_hash_t me_hash; PyObject *me_key; PyObject *me_value; /* This field is only meaningful for combined tables */ } PyDictKeyEntry; /* dict_lookup_func() returns index of entry which can be used like DK_ENTRIES(dk)[index]. * -1 when no entry found, -3 when compare raises error. */ typedef Py_ssize_t (*dict_lookup_func) (PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); #define DKIX_EMPTY (-1) #define DKIX_DUMMY (-2) /* Used internally */ #define DKIX_ERROR (-3) /* See dictobject.c for actual layout of DictKeysObject */ struct _dictkeysobject { Py_ssize_t dk_refcnt; /* Size of the hash table (dk_indices). It must be a power of 2. */ Py_ssize_t dk_size; /* Function to lookup in the hash table (dk_indices): - lookdict(): general-purpose, and may return DKIX_ERROR if (and only if) a comparison raises an exception. - lookdict_unicode(): specialized to Unicode string keys, comparison of which can never raise an exception; that function can never return DKIX_ERROR. - lookdict_unicode_nodummy(): similar to lookdict_unicode() but further specialized for Unicode string keys that cannot be the value. - lookdict_split(): Version of lookdict() for split tables. */ dict_lookup_func dk_lookup; /* Number of usable entries in dk_entries. */ Py_ssize_t dk_usable; /* Number of used entries in dk_entries. */ Py_ssize_t dk_nentries; /* Actual hash table of dk_size entries. It holds indices in dk_entries, or DKIX_EMPTY(-1) or DKIX_DUMMY(-2). Indices must be: 0 <= indice < USABLE_FRACTION(dk_size). The size in bytes of an indice depends on dk_size: - 1 byte if dk_size <= 0xff (char*) - 2 bytes if dk_size <= 0xffff (int16_t*) - 4 bytes if dk_size <= 0xffffffff (int32_t*) - 8 bytes otherwise (int64_t*) Dynamically sized, SIZEOF_VOID_P is minimum. */ char dk_indices[]; /* char is required to avoid strict aliasing. */ /* "PyDictKeyEntry dk_entries[dk_usable];" array follows: see the DK_ENTRIES() macro */ }; #endif python-frozendict/src/frozendict/c_src/3_9/cpython_src/Objects/dictobject.c000066400000000000000000001613631470250572200274050ustar00rootroot00000000000000/* Dictionary object implementation using a hash table */ /* The distribution includes a separate file, Objects/dictnotes.txt, describing explorations into dictionary design and optimization. It covers typical dictionary use patterns, the parameters for tuning dictionaries, and several ideas for possible optimizations. */ /* PyDictKeysObject This implements the dictionary's hashtable. As of Python 3.6, this is compact and ordered. Basic idea is described here: * https://mail.python.org/pipermail/python-dev/2012-December/123028.html * https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html layout: +---------------+ | dk_refcnt | | dk_size | | dk_lookup | | dk_usable | | dk_nentries | +---------------+ | dk_indices | | | +---------------+ | dk_entries | | | +---------------+ dk_indices is actual hashtable. It holds index in entries, or DKIX_EMPTY(-1) or DKIX_DUMMY(-2). Size of indices is dk_size. Type of each index in indices is vary on dk_size: * int8 for dk_size <= 128 * int16 for 256 <= dk_size <= 2**15 * int32 for 2**16 <= dk_size <= 2**31 * int64 for 2**32 <= dk_size dk_entries is array of PyDictKeyEntry. Its size is USABLE_FRACTION(dk_size). DK_ENTRIES(dk) can be used to get pointer to entries. NOTE: Since negative value is used for DKIX_EMPTY and DKIX_DUMMY, type of dk_indices entry is signed integer and int16 is used for table which dk_size == 256. */ /* The DictObject can be in one of two forms. Either: A combined table: ma_values == NULL, dk_refcnt == 1. Values are stored in the me_value field of the PyDictKeysObject. Or: A split table: ma_values != NULL, dk_refcnt >= 1 Values are stored in the ma_values array. Only string (unicode) keys are allowed. All dicts sharing same key must have same insertion order. There are four kinds of slots in the table (slot is index, and DK_ENTRIES(keys)[index] if index >= 0): 1. Unused. index == DKIX_EMPTY Does not hold an active (key, value) pair now and never did. Unused can transition to Active upon key insertion. This is each slot's initial state. 2. Active. index >= 0, me_key != NULL and me_value != NULL Holds an active (key, value) pair. Active can transition to Dummy or Pending upon key deletion (for combined and split tables respectively). This is the only case in which me_value != NULL. 3. Dummy. index == DKIX_DUMMY (combined only) Previously held an active (key, value) pair, but that was deleted and an active pair has not yet overwritten the slot. Dummy can transition to Active upon key insertion. Dummy slots cannot be made Unused again else the probe sequence in case of collision would have no way to know they were once active. 4. Pending. index >= 0, key != NULL, and value == NULL (split only) Not yet inserted in split-table. */ /* Preserving insertion order It's simple for combined table. Since dk_entries is mostly append only, we can get insertion order by just iterating dk_entries. One exception is .popitem(). It removes last item in dk_entries and decrement dk_nentries to achieve amortized O(1). Since there are DKIX_DUMMY remains in dk_indices, we can't increment dk_usable even though dk_nentries is decremented. In split table, inserting into pending entry is allowed only for dk_entries[ix] where ix == mp->ma_used. Inserting into other index and deleting item cause converting the dict to the combined table. */ /* PyDict_MINSIZE is the starting size for any new dict. * 8 allows dicts with no more than 5 active entries; experiments suggested * this suffices for the majority of dicts (consisting mostly of usually-small * dicts created to pass keyword arguments). * Making this 8, rather than 4 reduces the number of resizes for most * dictionaries, without any significant extra memory use. */ #define PyDict_MINSIZE 8 #include "Python.h" #include "dict-common.h" #include "stringlib/eq.h" // unicode_eq() /*[clinic input] class dict "PyDictObject *" "&PyDict_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=f157a5a0ce9589d6]*/ /* To ensure the lookup algorithm terminates, there must be at least one Unused slot (NULL key) in the table. To avoid slowing down lookups on a near-full table, we resize the table when it's USABLE_FRACTION (currently two-thirds) full. */ #define PERTURB_SHIFT 5 /* Major subtleties ahead: Most hash schemes depend on having a "good" hash function, in the sense of simulating randomness. Python doesn't: its most important hash functions (for ints) are very regular in common cases: >>>[hash(i) for i in range(4)] [0, 1, 2, 3] This isn't necessarily bad! To the contrary, in a table of size 2**i, taking the low-order i bits as the initial table index is extremely fast, and there are no collisions at all for dicts indexed by a contiguous range of ints. So this gives better-than-random behavior in common cases, and that's very desirable. OTOH, when collisions occur, the tendency to fill contiguous slices of the hash table makes a good collision resolution strategy crucial. Taking only the last i bits of the hash code is also vulnerable: for example, consider the list [i << 16 for i in range(20000)] as a set of keys. Since ints are their own hash codes, and this fits in a dict of size 2**15, the last 15 bits of every hash code are all 0: they *all* map to the same table index. But catering to unusual cases should not slow the usual ones, so we just take the last i bits anyway. It's up to collision resolution to do the rest. If we *usually* find the key we're looking for on the first try (and, it turns out, we usually do -- the table load factor is kept under 2/3, so the odds are solidly in our favor), then it makes best sense to keep the initial index computation dirt cheap. The first half of collision resolution is to visit table indices via this recurrence: j = ((5*j) + 1) mod 2**i For any initial j in range(2**i), repeating that 2**i times generates each int in range(2**i) exactly once (see any text on random-number generation for proof). By itself, this doesn't help much: like linear probing (setting j += 1, or j -= 1, on each loop trip), it scans the table entries in a fixed order. This would be bad, except that's not the only thing we do, and it's actually *good* in the common cases where hash keys are consecutive. In an example that's really too small to make this entirely clear, for a table of size 2**3 the order of indices is: 0 -> 1 -> 6 -> 7 -> 4 -> 5 -> 2 -> 3 -> 0 [and here it's repeating] If two things come in at index 5, the first place we look after is index 2, not 6, so if another comes in at index 6 the collision at 5 didn't hurt it. Linear probing is deadly in this case because there the fixed probe order is the *same* as the order consecutive keys are likely to arrive. But it's extremely unlikely hash codes will follow a 5*j+1 recurrence by accident, and certain that consecutive hash codes do not. The other half of the strategy is to get the other bits of the hash code into play. This is done by initializing a (unsigned) vrbl "perturb" to the full hash code, and changing the recurrence to: perturb >>= PERTURB_SHIFT; j = (5*j) + 1 + perturb; use j % 2**i as the next table index; Now the probe sequence depends (eventually) on every bit in the hash code, and the pseudo-scrambling property of recurring on 5*j+1 is more valuable, because it quickly magnifies small differences in the bits that didn't affect the initial index. Note that because perturb is unsigned, if the recurrence is executed often enough perturb eventually becomes and remains 0. At that point (very rarely reached) the recurrence is on (just) 5*j+1 again, and that's certain to find an empty slot eventually (since it generates every int in range(2**i), and we make sure there's always at least one empty slot). Selecting a good value for PERTURB_SHIFT is a balancing act. You want it small so that the high bits of the hash code continue to affect the probe sequence across iterations; but you want it large so that in really bad cases the high-order hash bits have an effect on early iterations. 5 was "the best" in minimizing total collisions across experiments Tim Peters ran (on both normal and pathological cases), but 4 and 6 weren't significantly worse. Historical: Reimer Behrends contributed the idea of using a polynomial-based approach, using repeated multiplication by x in GF(2**n) where an irreducible polynomial for each table size was chosen such that x was a primitive root. Christian Tismer later extended that to use division by x instead, as an efficient way to get the high bits of the hash code into play. This scheme also gave excellent collision statistics, but was more expensive: two if-tests were required inside the loop; computing "the next" index took about the same number of operations but without as much potential parallelism (e.g., computing 5*j can go on at the same time as computing 1+perturb in the above, and then shifting perturb can be done while the table index is being masked); and the PyDictObject struct required a member to hold the table's polynomial. In Tim's experiments the current scheme ran faster, produced equally good collision statistics, needed less code & used less memory. */ /* forward declarations */ static Py_ssize_t lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static Py_ssize_t lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); /*Global counter used to set ma_version_tag field of dictionary. * It is incremented each time that a dictionary is created and each * time that a dictionary is modified. */ static uint64_t pydict_global_version = 0; #define DICT_NEXT_VERSION() (++pydict_global_version) /* Dictionary reuse scheme to save calls to malloc and free */ #ifndef PyDict_MAXFREELIST #define PyDict_MAXFREELIST 80 #endif #if PyDict_MAXFREELIST > 0 static PyDictObject *free_list[PyDict_MAXFREELIST]; static int numfree = 0; static PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST]; static int numfreekeys = 0; #endif #include "clinic/dictobject.c.h" #define DK_SIZE(dk) ((dk)->dk_size) #if SIZEOF_VOID_P > 4 #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : DK_SIZE(dk) <= 0xffffffff ? \ 4 : sizeof(int64_t)) #else #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : sizeof(int32_t)) #endif #define DK_ENTRIES(dk) \ ((PyDictKeyEntry*)(&((int8_t*)((dk)->dk_indices))[DK_SIZE(dk) * DK_IXSIZE(dk)])) #define DK_MASK(dk) (((dk)->dk_size)-1) #define IS_POWER_OF_2(x) (((x) & (x-1)) == 0) static void free_keys_object(PyDictKeysObject *keys); static inline void dictkeys_incref(PyDictKeysObject *dk) { #ifdef Py_REF_DEBUG _Py_RefTotal++; #endif dk->dk_refcnt++; } static inline void dictkeys_decref(PyDictKeysObject *dk) { assert(dk->dk_refcnt > 0); #ifdef Py_REF_DEBUG _Py_RefTotal--; #endif if (--dk->dk_refcnt == 0) { free_keys_object(dk); } } /* lookup indices. returns DKIX_EMPTY, DKIX_DUMMY, or ix >=0 */ static inline Py_ssize_t dictkeys_get_index(const PyDictKeysObject *keys, Py_ssize_t i) { Py_ssize_t s = DK_SIZE(keys); Py_ssize_t ix; if (s <= 0xff) { const int8_t *indices = (const int8_t*)(keys->dk_indices); ix = indices[i]; } else if (s <= 0xffff) { const int16_t *indices = (const int16_t*)(keys->dk_indices); ix = indices[i]; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { const int64_t *indices = (const int64_t*)(keys->dk_indices); ix = indices[i]; } #endif else { const int32_t *indices = (const int32_t*)(keys->dk_indices); ix = indices[i]; } assert(ix >= DKIX_DUMMY); return ix; } /* write to indices. */ static inline void dictkeys_set_index(PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix) { Py_ssize_t s = DK_SIZE(keys); assert(ix >= DKIX_DUMMY); if (s <= 0xff) { int8_t *indices = (int8_t*)(keys->dk_indices); assert(ix <= 0x7f); indices[i] = (char)ix; } else if (s <= 0xffff) { int16_t *indices = (int16_t*)(keys->dk_indices); assert(ix <= 0x7fff); indices[i] = (int16_t)ix; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { int64_t *indices = (int64_t*)(keys->dk_indices); indices[i] = ix; } #endif else { int32_t *indices = (int32_t*)(keys->dk_indices); assert(ix <= 0x7fffffff); indices[i] = (int32_t)ix; } } /* USABLE_FRACTION is the maximum dictionary load. * Increasing this ratio makes dictionaries more dense resulting in more * collisions. Decreasing it improves sparseness at the expense of spreading * indices over more cache lines and at the cost of total memory consumed. * * USABLE_FRACTION must obey the following: * (0 < USABLE_FRACTION(n) < n) for all n >= 2 * * USABLE_FRACTION should be quick to calculate. * Fractions around 1/2 to 2/3 seem to work well in practice. */ #define USABLE_FRACTION(n) (((n) << 1)/3) /* Find the smallest dk_size >= minsize. */ static inline Py_ssize_t calculate_keysize(Py_ssize_t minsize) { #if SIZEOF_LONG == SIZEOF_SIZE_T minsize = (minsize | PyDict_MINSIZE) - 1; return 1LL << _Py_bit_length(minsize | (PyDict_MINSIZE-1)); #elif defined(_MSC_VER) // On 64bit Windows, sizeof(long) == 4. minsize = (minsize | PyDict_MINSIZE) - 1; unsigned long msb; _BitScanReverse64(&msb, (uint64_t)minsize); return 1LL << (msb + 1); #else Py_ssize_t size; for (size = PyDict_MINSIZE; size < minsize && size > 0; size <<= 1) ; return size; #endif } /* estimate_keysize is reverse function of USABLE_FRACTION. * * This can be used to reserve enough size to insert n entries without * resizing. */ static inline Py_ssize_t estimate_keysize(Py_ssize_t n) { return calculate_keysize((n*3 + 1) / 2); } /* GROWTH_RATE. Growth rate upon hitting maximum load. * Currently set to used*3. * This means that dicts double in size when growing without deletions, * but have more head room when the number of deletions is on a par with the * number of insertions. See also bpo-17563 and bpo-33205. * * GROWTH_RATE was set to used*4 up to version 3.2. * GROWTH_RATE was set to used*2 in version 3.3.0 * GROWTH_RATE was set to used*2 + capacity/2 in 3.4.0-3.6.0. */ #define GROWTH_RATE(d) ((d)->ma_used*3) /* This immutable, empty PyDictKeysObject is used for PyDict_Clear() * (which cannot fail and thus can do no allocation). */ static PyDictKeysObject empty_keys_struct = { 1, /* dk_refcnt */ 1, /* dk_size */ lookdict_unicode_nodummy, /* dk_lookup */ 0, /* dk_usable (immutable) */ 0, /* dk_nentries */ {DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY}, /* dk_indices */ }; static PyObject *empty_values[1] = { NULL }; #define Py_EMPTY_KEYS &empty_keys_struct /* Uncomment to check the dict content in _PyDict_CheckConsistency() */ /* #define DEBUG_PYDICT */ #ifdef DEBUG_PYDICT # define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 1)) #else # define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 0)) #endif static int _PyDict_CheckConsistency(PyObject *op, int check_content) { #define CHECK(expr) \ do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0) assert(op != NULL); CHECK(PyAnyDict_Check(op)); PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; int splitted = _PyDict_HasSplitTable(mp); Py_ssize_t usable = USABLE_FRACTION(keys->dk_size); CHECK(0 <= mp->ma_used && mp->ma_used <= usable); CHECK(IS_POWER_OF_2(keys->dk_size)); CHECK(0 <= keys->dk_usable && keys->dk_usable <= usable); CHECK(0 <= keys->dk_nentries && keys->dk_nentries <= usable); CHECK(keys->dk_usable + keys->dk_nentries <= usable); if (!splitted) { /* combined table */ CHECK(keys->dk_refcnt == 1); } if (check_content) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i; for (i=0; i < keys->dk_size; i++) { Py_ssize_t ix = dictkeys_get_index(keys, i); CHECK(DKIX_DUMMY <= ix && ix <= usable); } for (i=0; i < usable; i++) { PyDictKeyEntry *entry = &entries[i]; PyObject *key = entry->me_key; if (key != NULL) { if (PyUnicode_CheckExact(key)) { Py_hash_t hash = ((PyASCIIObject *)key)->hash; CHECK(hash != -1); CHECK(entry->me_hash == hash); } else { /* test_dict fails if PyObject_Hash() is called again */ CHECK(entry->me_hash != -1); } if (!splitted) { CHECK(entry->me_value != NULL); } } if (splitted) { CHECK(entry->me_value == NULL); } } if (splitted) { /* splitted table */ for (i=0; i < mp->ma_used; i++) { CHECK(mp->ma_values[i] != NULL); } } } return 1; #undef CHECK } static PyDictKeysObject* new_keys_object(Py_ssize_t size) { PyDictKeysObject *dk; Py_ssize_t es, usable; assert(size >= PyDict_MINSIZE); assert(IS_POWER_OF_2(size)); usable = USABLE_FRACTION(size); if (size <= 0xff) { es = 1; } else if (size <= 0xffff) { es = 2; } #if SIZEOF_VOID_P > 4 else if (size <= 0xffffffff) { es = 4; } #endif else { es = sizeof(Py_ssize_t); } #if PyDict_MAXFREELIST > 0 if (size == PyDict_MINSIZE && numfreekeys > 0) { dk = keys_free_list[--numfreekeys]; } else #endif { dk = PyObject_Malloc(sizeof(PyDictKeysObject) + es * size + sizeof(PyDictKeyEntry) * usable); if (dk == NULL) { PyErr_NoMemory(); return NULL; } } #ifdef Py_REF_DEBUG _Py_RefTotal++; #endif dk->dk_refcnt = 1; dk->dk_size = size; dk->dk_usable = usable; dk->dk_lookup = lookdict_unicode_nodummy; dk->dk_nentries = 0; memset(&dk->dk_indices[0], 0xff, es * size); memset(DK_ENTRIES(dk), 0, sizeof(PyDictKeyEntry) * usable); return dk; } static void free_keys_object(PyDictKeysObject *keys) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n; for (i = 0, n = keys->dk_nentries; i < n; i++) { Py_XDECREF(entries[i].me_key); Py_XDECREF(entries[i].me_value); } #if PyDict_MAXFREELIST > 0 if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) { keys_free_list[numfreekeys++] = keys; return; } #endif PyObject_Free(keys); } #define new_values(size) PyMem_NEW(PyObject *, size) #define free_values(values) PyMem_Free(values) static Py_ssize_t _d_PyDict_KeysSize(PyDictKeysObject *keys) { return (sizeof(PyDictKeysObject) + DK_IXSIZE(keys) * DK_SIZE(keys) + USABLE_FRACTION(DK_SIZE(keys)) * sizeof(PyDictKeyEntry)); } static PyDictKeysObject * clone_combined_dict_keys(PyDictObject *orig) { assert(PyAnyDict_Check(orig)); assert( Py_TYPE(orig)->tp_iter == PyDict_Type.tp_iter || Py_TYPE(orig)->tp_iter == (getiterfunc)frozendict_iter ); assert(orig->ma_values == NULL); assert(orig->ma_keys->dk_refcnt == 1); Py_ssize_t keys_size = _d_PyDict_KeysSize(orig->ma_keys); PyDictKeysObject *keys = PyObject_Malloc(keys_size); if (keys == NULL) { PyErr_NoMemory(); return NULL; } memcpy(keys, orig->ma_keys, keys_size); /* After copying key/value pairs, we need to incref all keys and values and they are about to be co-owned by a new dict object. */ PyDictKeyEntry *ep0 = DK_ENTRIES(keys); Py_ssize_t n = keys->dk_nentries; for (Py_ssize_t i = 0; i < n; i++) { PyDictKeyEntry *entry = &ep0[i]; PyObject *value = entry->me_value; Py_INCREF(value); Py_INCREF(entry->me_key); } /* Since we copied the keys table we now have an extra reference in the system. Manually call increment _Py_RefTotal to signal that we have it now; calling dictkeys_incref would be an error as keys->dk_refcnt is already set to 1 (after memcpy). */ #ifdef Py_REF_DEBUG _Py_RefTotal++; #endif return keys; } /* The basic lookup function used by all operations. This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4. Open addressing is preferred over chaining since the link overhead for chaining would be substantial (100% with typical malloc overhead). The initial probe index is computed as hash mod the table size. Subsequent probe indices are computed as explained earlier. All arithmetic on hash should ignore overflow. The details in this version are due to Tim Peters, building on many past contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and Christian Tismer. lookdict() is general-purpose, and may return DKIX_ERROR if (and only if) a comparison raises an exception. lookdict_unicode() below is specialized to string keys, comparison of which can never raise an exception; that function can never return DKIX_ERROR when key is string. Otherwise, it falls back to lookdict(). lookdict_unicode_nodummy is further specialized for string keys that cannot be the value. For both, when the key isn't found a DKIX_EMPTY is returned. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { size_t i, mask, perturb; PyDictKeysObject *dk; PyDictKeyEntry *ep0; top: dk = mp->ma_keys; ep0 = DK_ENTRIES(dk); mask = DK_MASK(dk); perturb = hash; i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(dk, i); if (ix == DKIX_EMPTY) { *value_addr = NULL; return ix; } if (ix >= 0) { PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); if (ep->me_key == key) { *value_addr = ep->me_value; return ix; } if (ep->me_hash == hash) { PyObject *startkey = ep->me_key; Py_INCREF(startkey); int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); Py_DECREF(startkey); if (cmp < 0) { *value_addr = NULL; return DKIX_ERROR; } if (dk == mp->ma_keys && ep->me_key == startkey) { if (cmp > 0) { *value_addr = ep->me_value; return ix; } } else { /* The dict was mutated, restart */ goto top; } } } perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; } Py_UNREACHABLE(); } /* Faster version of lookdict_unicode when it is known that no keys * will be present. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { assert(mp->ma_values == NULL); /* Make sure this function doesn't have to handle non-unicode keys, including subclasses of str; e.g., one reason to subclass unicodes is to override __eq__, and for speed we don't cater to that here. */ if (!PyUnicode_CheckExact(key)) { return lookdict(mp, key, hash, value_addr); } PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); size_t mask = DK_MASK(mp->ma_keys); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i); assert (ix != DKIX_DUMMY); if (ix == DKIX_EMPTY) { *value_addr = NULL; return DKIX_EMPTY; } PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { *value_addr = ep->me_value; return ix; } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } #define MAINTAIN_TRACKING(mp, key, value) \ do { \ if (!_PyObject_GC_IS_TRACKED(mp)) { \ if (_PyObject_GC_MAY_BE_TRACKED(key) || \ _PyObject_GC_MAY_BE_TRACKED(value)) { \ PyObject_GC_Track(mp); \ } \ } \ } while(0) /* Internal function to find slot for an item from its hash when it is known that the key is not present in the dict. The dict must be combined. */ static Py_ssize_t find_empty_slot(PyDictKeysObject *keys, Py_hash_t hash) { assert(keys != NULL); const size_t mask = DK_MASK(keys); size_t i = hash & mask; Py_ssize_t ix = dictkeys_get_index(keys, i); for (size_t perturb = hash; ix >= 0;) { perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; ix = dictkeys_get_index(keys, i); } return i; } /* Internal routine used by dictresize() to build a hashtable of entries. */ static void build_indices(PyDictKeysObject *keys, PyDictKeyEntry *ep, Py_ssize_t n) { size_t mask = (size_t)DK_SIZE(keys) - 1; for (Py_ssize_t ix = 0; ix != n; ix++, ep++) { Py_hash_t hash = ep->me_hash; size_t i = hash & mask; for (size_t perturb = hash; dictkeys_get_index(keys, i) != DKIX_EMPTY;) { perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } dictkeys_set_index(keys, i, ix); } } static int _d_PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue, Py_hash_t *phash) { Py_ssize_t i; PyDictObject *mp; PyDictKeyEntry *entry_ptr; PyObject *value; if (!PyAnyDict_Check(op)) return 0; mp = (PyDictObject *)op; i = *ppos; if (mp->ma_values) { if (i < 0 || i >= mp->ma_used) return 0; /* values of split table is always dense */ entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; value = mp->ma_values[i]; assert(value != NULL); } else { Py_ssize_t n = mp->ma_keys->dk_nentries; if (i < 0 || i >= n) return 0; entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) return 0; value = entry_ptr->me_value; } *ppos = i+1; if (pkey) *pkey = entry_ptr->me_key; if (phash) *phash = entry_ptr->me_hash; if (pvalue) *pvalue = value; return 1; } /* Methods */ static void dict_dealloc(PyDictObject *mp) { PyObject **values = mp->ma_values; PyDictKeysObject *keys = mp->ma_keys; Py_ssize_t i, n; /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(mp); Py_TRASHCAN_BEGIN(mp, dict_dealloc) if (values != NULL) { if (values != empty_values) { for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) { Py_XDECREF(values[i]); } free_values(values); } dictkeys_decref(keys); } else if (keys != NULL) { assert(keys->dk_refcnt == 1 || keys == Py_EMPTY_KEYS); dictkeys_decref(keys); } #if PyDict_MAXFREELIST > 0 if (numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type)) { free_list[numfree++] = mp; } else #endif { Py_TYPE(mp)->tp_free((PyObject *)mp); } Py_TRASHCAN_END } static PyObject * dict_repr(PyDictObject *mp) { Py_ssize_t i; PyObject *key = NULL, *value = NULL; _PyUnicodeWriter writer; int first; i = Py_ReprEnter((PyObject *)mp); if (i != 0) { return i > 0 ? PyUnicode_FromString("{...}") : NULL; } if (mp->ma_used == 0) { Py_ReprLeave((PyObject *)mp); return PyUnicode_FromString("{}"); } _PyUnicodeWriter_Init(&writer); writer.overallocate = 1; /* "{" + "1: 2" + ", 3: 4" * (len - 1) + "}" */ writer.min_length = 1 + 4 + (2 + 4) * (mp->ma_used - 1) + 1; if (_PyUnicodeWriter_WriteChar(&writer, '{') < 0) goto error; /* Do repr() on each key+value pair, and insert ": " between them. Note that repr may mutate the dict. */ i = 0; first = 1; while (_d_PyDict_Next((PyObject *)mp, &i, &key, &value, NULL)) { PyObject *s; int res; /* Prevent repr from deleting key or value during key format. */ Py_INCREF(key); Py_INCREF(value); if (!first) { if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0) goto error; } first = 0; s = PyObject_Repr(key); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; if (_PyUnicodeWriter_WriteASCIIString(&writer, ": ", 2) < 0) goto error; s = PyObject_Repr(value); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; Py_CLEAR(key); Py_CLEAR(value); } writer.overallocate = 0; if (_PyUnicodeWriter_WriteChar(&writer, '}') < 0) goto error; Py_ReprLeave((PyObject *)mp); return _PyUnicodeWriter_Finish(&writer); error: Py_ReprLeave((PyObject *)mp); _PyUnicodeWriter_Dealloc(&writer); Py_XDECREF(key); Py_XDECREF(value); return NULL; } static Py_ssize_t dict_length(PyDictObject *mp) { return mp->ma_used; } static PyObject * dict_subscript(PyDictObject *mp, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || value == NULL) { if (!PyAnyDict_CheckExact(mp)) { /* Look up __missing__ method if we're a subclass. */ PyObject *missing, *res; _Py_IDENTIFIER(__missing__); missing = _PyObject_LookupSpecial((PyObject *)mp, &PyId___missing__); if (missing != NULL) { res = PyObject_CallOneArg(missing, key); Py_DECREF(missing); return res; } else if (PyErr_Occurred()) return NULL; } _PyErr_SetKeyError(key); return NULL; } Py_INCREF(value); return value; } static PyObject * dict_richcompare(PyObject *v, PyObject *w, int op) { int cmp; PyObject *res; if (!PyAnyDict_Check(v) || !PyAnyDict_Check(w)) { res = Py_NotImplemented; } else if (op == Py_EQ || op == Py_NE) { cmp = frozendict_equal((PyDictObject *)v, (PyDictObject *)w); if (cmp < 0) return NULL; res = (cmp == (op == Py_EQ)) ? Py_True : Py_False; } else res = Py_NotImplemented; Py_INCREF(res); return res; } /*[clinic input] @coexist dict.__contains__ key: object / True if the dictionary has the specified key, else False. [clinic start generated code]*/ static PyObject * dict___contains__(PyDictObject *self, PyObject *key) /*[clinic end generated code: output=a3d03db709ed6e6b input=fe1cb42ad831e820]*/ { register PyDictObject *mp = self; Py_hash_t hash; Py_ssize_t ix; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || value == NULL) Py_RETURN_FALSE; Py_RETURN_TRUE; } /*[clinic input] dict.get key: object default: object = None / Return the value for key if key is in the dictionary, else default. [clinic start generated code]*/ static PyObject * dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value) /*[clinic end generated code: output=bba707729dee05bf input=279ddb5790b6b107]*/ { PyObject *val = NULL; Py_hash_t hash; Py_ssize_t ix; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (self->ma_keys->dk_lookup) (self, key, hash, &val); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || val == NULL) { val = default_value; } Py_INCREF(val); return val; } static int dict_traverse(PyObject *op, visitproc visit, void *arg) { PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n = keys->dk_nentries; if (keys->dk_lookup == lookdict) { for (i = 0; i < n; i++) { if (entries[i].me_value != NULL) { Py_VISIT(entries[i].me_value); Py_VISIT(entries[i].me_key); } } } else { if (mp->ma_values != NULL) { for (i = 0; i < n; i++) { Py_VISIT(mp->ma_values[i]); } } else { for (i = 0; i < n; i++) { Py_VISIT(entries[i].me_value); } } } return 0; } static PyObject *dictiter_new(PyDictObject *, PyTypeObject *); static PyObject * dict_sizeof(PyDictObject *mp, PyObject *Py_UNUSED(ignored)) { return PyLong_FromSsize_t(_PyDict_SizeOf(mp)); } PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]"); PyDoc_STRVAR(sizeof__doc__, "D.__sizeof__() -> size of D in memory, in bytes"); PyDoc_STRVAR(copy__doc__, "D.copy() -> a shallow copy of D"); PyDoc_STRVAR(keys__doc__, "D.keys() -> a set-like object providing a view on D's keys"); PyDoc_STRVAR(items__doc__, "D.items() -> a set-like object providing a view on D's items"); PyDoc_STRVAR(values__doc__, "D.values() -> an object providing a view on D's values"); /* Hack to implement "key in dict" */ static PySequenceMethods dict_as_sequence = { 0, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ PyDict_Contains, /* sq_contains */ 0, /* sq_inplace_concat */ 0, /* sq_inplace_repeat */ }; /* Dictionary iterator types */ typedef struct { PyObject_HEAD PyDictObject *di_dict; /* Set to NULL when iterator is exhausted */ Py_ssize_t di_used; Py_ssize_t di_pos; PyObject* di_result; /* reusable result tuple for iteritems */ Py_ssize_t len; } dictiterobject; static PyObject * dictiter_new(PyDictObject *dict, PyTypeObject *itertype) { dictiterobject *di; di = PyObject_GC_New(dictiterobject, itertype); if (di == NULL) { return NULL; } Py_INCREF(dict); di->di_dict = dict; di->di_used = dict->ma_used; di->len = dict->ma_used; if (itertype == &PyDictRevIterKey_Type || itertype == &PyDictRevIterItem_Type || itertype == &PyDictRevIterValue_Type) { if (dict->ma_values) { di->di_pos = dict->ma_used - 1; } else { di->di_pos = dict->ma_keys->dk_nentries - 1; } } else { di->di_pos = 0; } if ( itertype == &PyFrozenDictIterItem_Type || itertype == &PyDictRevIterItem_Type ) { di->di_result = PyTuple_Pack(2, Py_None, Py_None); if (di->di_result == NULL) { Py_DECREF(di); return NULL; } } else { di->di_result = NULL; } PyObject_GC_Track(di); return (PyObject *)di; } static void dictiter_dealloc(dictiterobject *di) { /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(di); Py_XDECREF(di->di_dict); Py_XDECREF(di->di_result); PyObject_GC_Del(di); } static int dictiter_traverse(dictiterobject *di, visitproc visit, void *arg) { Py_VISIT(di->di_dict); Py_VISIT(di->di_result); return 0; } static PyObject * dictiter_len(dictiterobject *di, PyObject *Py_UNUSED(ignored)) { Py_ssize_t len = 0; if (di->di_dict != NULL && di->di_used == di->di_dict->ma_used) len = di->len; return PyLong_FromSize_t(len); } PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); static PyObject * dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyMethodDef dictiter_methods[] = { {"__length_hint__", (PyCFunction)(void(*)(void))dictiter_len, METH_NOARGS, length_hint_doc}, {"__reduce__", (PyCFunction)(void(*)(void))dictiter_reduce, METH_NOARGS, reduce_doc}, {NULL, NULL} /* sentinel */ }; /* dictreviter */ /*[clinic input] dict.__reversed__ Return a reverse iterator over the dict keys. [clinic start generated code]*/ static PyObject * dict___reversed___impl(PyDictObject *self) /*[clinic end generated code: output=e674483336d1ed51 input=23210ef3477d8c4d]*/ { assert (PyAnyDict_Check(self)); return dictiter_new(self, &PyDictRevIterKey_Type); } static PyObject * dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)) { _Py_IDENTIFIER(iter); /* copy the iterator state */ dictiterobject tmp = *di; Py_XINCREF(tmp.di_dict); PyObject *list = PySequence_List((PyObject*)&tmp); Py_XDECREF(tmp.di_dict); if (list == NULL) { return NULL; } return Py_BuildValue("N(N)", _PyEval_GetBuiltinId(&PyId_iter), list); } /***********************************************/ /* View objects for keys(), items(), values(). */ /***********************************************/ /* The instance lay-out is the same for all three; but the type differs. */ static void dictview_dealloc(_PyDictViewObject *dv) { /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(dv); Py_XDECREF(dv->dv_dict); PyObject_GC_Del(dv); } static int dictview_traverse(_PyDictViewObject *dv, visitproc visit, void *arg) { Py_VISIT(dv->dv_dict); return 0; } static Py_ssize_t dictview_len(_PyDictViewObject *dv) { Py_ssize_t len = 0; if (dv->dv_dict != NULL) len = dv->dv_dict->ma_used; return len; } static PyObject * _d_PyDictView_New(PyObject *dict, PyTypeObject *type) { _PyDictViewObject *dv; if (dict == NULL) { PyErr_BadInternalCall(); return NULL; } if (!PyAnyDict_Check(dict)) { /* XXX Get rid of this restriction later */ PyErr_Format(PyExc_TypeError, "%s() requires a dict argument, not '%s'", type->tp_name, Py_TYPE(dict)->tp_name); return NULL; } dv = PyObject_GC_New(_PyDictViewObject, type); if (dv == NULL) return NULL; Py_INCREF(dict); dv->dv_dict = (PyDictObject *)dict; PyObject_GC_Track(dv); return (PyObject *)dv; } static PyObject * dictview_mapping(PyObject *view, void *Py_UNUSED(ignored)) { assert(view != NULL); assert(PyAnyDictKeys_Check(view) || PyAnyDictValues_Check(view) || PyAnyDictItems_Check(view)); PyObject *mapping = (PyObject *)((_PyDictViewObject *)view)->dv_dict; return PyDictProxy_New(mapping); } static PyGetSetDef dictview_getset[] = { {"mapping", dictview_mapping, (setter)NULL, "dictionary that this view refers to", NULL}, {0} }; /* TODO(guido): The views objects are not complete: * support more set operations * support arbitrary mappings? - either these should be static or exported in dictobject.h - if public then they should probably be in builtins */ /* Return 1 if self is a subset of other, iterating over self; 0 if not; -1 if an error occurred. */ static int all_contained_in(PyObject *self, PyObject *other) { PyObject *iter = PyObject_GetIter(self); int ok = 1; if (iter == NULL) return -1; for (;;) { PyObject *next = PyIter_Next(iter); if (next == NULL) { if (PyErr_Occurred()) ok = -1; break; } ok = PySequence_Contains(other, next); Py_DECREF(next); if (ok <= 0) break; } Py_DECREF(iter); return ok; } static PyObject * dictview_richcompare(PyObject *self, PyObject *other, int op) { Py_ssize_t len_self, len_other; int ok; PyObject *result; assert(self != NULL); assert(PyAnyDictViewSet_Check(self)); assert(other != NULL); if (!PyAnySet_Check(other) && !PyAnyDictViewSet_Check(other)) Py_RETURN_NOTIMPLEMENTED; len_self = PyObject_Size(self); if (len_self < 0) return NULL; len_other = PyObject_Size(other); if (len_other < 0) return NULL; ok = 0; switch(op) { case Py_NE: case Py_EQ: if (len_self == len_other) ok = all_contained_in(self, other); if (op == Py_NE && ok >= 0) ok = !ok; break; case Py_LT: if (len_self < len_other) ok = all_contained_in(self, other); break; case Py_LE: if (len_self <= len_other) ok = all_contained_in(self, other); break; case Py_GT: if (len_self > len_other) ok = all_contained_in(other, self); break; case Py_GE: if (len_self >= len_other) ok = all_contained_in(other, self); break; } if (ok < 0) return NULL; result = ok ? Py_True : Py_False; Py_INCREF(result); return result; } static PyObject * dictview_repr(_PyDictViewObject *dv) { PyObject *seq; PyObject *result = NULL; Py_ssize_t rc; rc = Py_ReprEnter((PyObject *)dv); if (rc != 0) { return rc > 0 ? PyUnicode_FromString("...") : NULL; } seq = PySequence_List((PyObject *)dv); if (seq == NULL) { goto Done; } result = PyUnicode_FromFormat("%s(%R)", Py_TYPE(dv)->tp_name, seq); Py_DECREF(seq); Done: Py_ReprLeave((PyObject *)dv); return result; } /*** dict_keys ***/ static int dictkeys_contains(_PyDictViewObject *dv, PyObject *obj) { if (dv->dv_dict == NULL) return 0; return PyDict_Contains((PyObject *)dv->dv_dict, obj); } static PySequenceMethods dictkeys_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictkeys_contains, /* sq_contains */ }; // Create an set object from dictviews object. // Returns a new reference. // This utility function is used by set operations. static PyObject* dictviews_to_set(PyObject *self) { PyObject *left = self; if (PyAnyDictKeys_Check(self)) { // PySet_New() has fast path for the dict object. PyObject *dict = (PyObject *)((_PyDictViewObject *)self)->dv_dict; if (PyAnyDict_CheckExact(dict)) { left = dict; } } return PySet_New(left); } static PyObject* dictviews_sub(PyObject *self, PyObject *other) { PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } _Py_IDENTIFIER(difference_update); PyObject *tmp = _PyObject_CallMethodIdOneArg( result, &PyId_difference_update, other); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static int dictitems_contains(_PyDictViewObject *dv, PyObject *obj); static PyObject * _d_PyDictView_Intersect(PyObject* self, PyObject *other) { PyObject *result; PyObject *it; PyObject *key; Py_ssize_t len_self; int rv; int (*dict_contains)(_PyDictViewObject *, PyObject *); /* Python interpreter swaps parameters when dict view is on right side of & */ if (!PyAnyDictViewSet_Check(self)) { PyObject *tmp = other; other = self; self = tmp; } len_self = dictview_len((_PyDictViewObject *)self); /* if other is a set and self is smaller than other, reuse set intersection logic */ if (PySet_CheckExact(other) && len_self <= PyObject_Size(other)) { _Py_IDENTIFIER(intersection); return _PyObject_CallMethodIdObjArgs(other, &PyId_intersection, self, NULL); } /* if other is another dict view, and it is bigger than self, swap them */ if (PyAnyDictViewSet_Check(other)) { Py_ssize_t len_other = dictview_len((_PyDictViewObject *)other); if (len_other > len_self) { PyObject *tmp = other; other = self; self = tmp; } } /* at this point, two things should be true 1. self is a dictview 2. if other is a dictview then it is smaller than self */ result = PySet_New(NULL); if (result == NULL) return NULL; it = PyObject_GetIter(other); if (it == NULL) { Py_DECREF(result); return NULL; } if (PyAnyDictKeys_Check(self)) { dict_contains = dictkeys_contains; } /* else PyDictItems_Check(self) */ else { dict_contains = dictitems_contains; } while ((key = PyIter_Next(it)) != NULL) { rv = dict_contains((_PyDictViewObject *)self, key); if (rv < 0) { goto error; } if (rv) { if (PySet_Add(result, key)) { goto error; } } Py_DECREF(key); } Py_DECREF(it); if (PyErr_Occurred()) { Py_DECREF(result); return NULL; } return result; error: Py_DECREF(it); Py_DECREF(result); Py_DECREF(key); return NULL; } static PyObject* dictviews_or(PyObject* self, PyObject *other) { PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } if (_PySet_Update(result, other) < 0) { Py_DECREF(result); return NULL; } return result; } static PyObject * dictitems_xor(PyObject *self, PyObject *other) { assert(PyAnyDictItems_Check(self)); assert(PyAnyDictItems_Check(other)); PyObject *d1 = (PyObject *)((_PyDictViewObject *)self)->dv_dict; PyObject *d2 = (PyObject *)((_PyDictViewObject *)other)->dv_dict; PyObject *temp_dict; if (PyDict_CheckExact(d1)) { temp_dict = PyDict_Copy(d1); } else { PyObject* args = PyTuple_New(1); if (args == NULL) { return NULL; } Py_INCREF(d1); PyTuple_SET_ITEM(args, 0, d1); temp_dict = PyObject_Call((PyObject *) &PyDict_Type, args, NULL); Py_DECREF(args); } if (temp_dict == NULL) { return NULL; } PyObject *result_set = PySet_New(NULL); if (result_set == NULL) { Py_CLEAR(temp_dict); return NULL; } PyObject *key = NULL, *val1 = NULL, *val2 = NULL; Py_ssize_t pos = 0; Py_hash_t hash; while (_d_PyDict_Next(d2, &pos, &key, &val2, &hash)) { Py_INCREF(key); Py_INCREF(val2); val1 = _PyDict_GetItem_KnownHash(temp_dict, key, hash); int to_delete; if (val1 == NULL) { if (PyErr_Occurred()) { goto error; } to_delete = 0; } else { Py_INCREF(val1); to_delete = PyObject_RichCompareBool(val1, val2, Py_EQ); if (to_delete < 0) { goto error; } } if (to_delete) { if (_PyDict_DelItem_KnownHash(temp_dict, key, hash) < 0) { goto error; } } else { PyObject *pair = PyTuple_Pack(2, key, val2); if (pair == NULL) { goto error; } if (PySet_Add(result_set, pair) < 0) { Py_DECREF(pair); goto error; } Py_DECREF(pair); } Py_DECREF(key); Py_XDECREF(val1); Py_DECREF(val2); } key = val1 = val2 = NULL; _Py_IDENTIFIER(items); PyObject *remaining_pairs = _PyObject_CallMethodIdNoArgs(temp_dict, &PyId_items); if (remaining_pairs == NULL) { goto error; } if (_PySet_Update(result_set, remaining_pairs) < 0) { Py_DECREF(remaining_pairs); goto error; } Py_DECREF(temp_dict); Py_DECREF(remaining_pairs); return result_set; error: Py_XDECREF(temp_dict); Py_XDECREF(result_set); Py_XDECREF(key); Py_XDECREF(val1); Py_XDECREF(val2); return NULL; } static PyObject* dictviews_xor(PyObject* self, PyObject *other) { if (PyAnyDictItems_Check(self) && PyAnyDictItems_Check(other)) { return dictitems_xor(self, other); } PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } _Py_IDENTIFIER(symmetric_difference_update); PyObject *tmp = _PyObject_CallMethodIdOneArg( result, &PyId_symmetric_difference_update, other); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static PyNumberMethods dictviews_as_number = { 0, /*nb_add*/ (binaryfunc)dictviews_sub, /*nb_subtract*/ 0, /*nb_multiply*/ 0, /*nb_remainder*/ 0, /*nb_divmod*/ 0, /*nb_power*/ 0, /*nb_negative*/ 0, /*nb_positive*/ 0, /*nb_absolute*/ 0, /*nb_bool*/ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ (binaryfunc)_d_PyDictView_Intersect,/*nb_and*/ (binaryfunc)dictviews_xor, /*nb_xor*/ (binaryfunc)dictviews_or, /*nb_or*/ }; static PyObject* dictviews_isdisjoint(PyObject *self, PyObject *other) { PyObject *it; PyObject *item = NULL; if (self == other) { if (dictview_len((_PyDictViewObject *)self) == 0) Py_RETURN_TRUE; else Py_RETURN_FALSE; } /* Iterate over the shorter object (only if other is a set, * because PySequence_Contains may be expensive otherwise): */ if (PyAnySet_Check(other) || PyAnyDictViewSet_Check(other)) { Py_ssize_t len_self = dictview_len((_PyDictViewObject *)self); Py_ssize_t len_other = PyObject_Size(other); if (len_other == -1) return NULL; if ((len_other > len_self)) { PyObject *tmp = other; other = self; self = tmp; } } it = PyObject_GetIter(other); if (it == NULL) return NULL; while ((item = PyIter_Next(it)) != NULL) { int contains = PySequence_Contains(self, item); Py_DECREF(item); if (contains == -1) { Py_DECREF(it); return NULL; } if (contains) { Py_DECREF(it); Py_RETURN_FALSE; } } Py_DECREF(it); if (PyErr_Occurred()) return NULL; /* PyIter_Next raised an exception. */ Py_RETURN_TRUE; } PyDoc_STRVAR(isdisjoint_doc, "Return True if the view and the given iterable have a null intersection."); static PyObject* dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_keys_doc, "Return a reverse iterator over the dict keys."); static PyMethodDef dictkeys_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {"__reversed__", (PyCFunction)(void(*)(void))dictkeys_reversed, METH_NOARGS, reversed_keys_doc}, {NULL, NULL} /* sentinel */ }; static PyObject * dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterKey_Type); } /*** dict_items ***/ /* Variant of PyDict_GetItem() that doesn't suppress exceptions. This returns NULL *with* an exception set if an exception occurred. It returns NULL *without* an exception set if the key wasn't present. */ static PyObject * d_PyDict_GetItemWithError(PyObject *op, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyDictObject*mp = (PyDictObject *)op; PyObject *value; if (!PyAnyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { return NULL; } } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix < 0) return NULL; return value; } static int dictitems_contains(_PyDictViewObject *dv, PyObject *obj) { int result; PyObject *key, *value, *found; if (dv->dv_dict == NULL) return 0; if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) return 0; key = PyTuple_GET_ITEM(obj, 0); value = PyTuple_GET_ITEM(obj, 1); found = d_PyDict_GetItemWithError((PyObject *)dv->dv_dict, key); if (found == NULL) { if (PyErr_Occurred()) return -1; return 0; } Py_INCREF(found); result = PyObject_RichCompareBool(found, value, Py_EQ); Py_DECREF(found); return result; } static PySequenceMethods dictitems_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictitems_contains, /* sq_contains */ }; static PyObject* dictitems_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_items_doc, "Return a reverse iterator over the dict items."); static PyMethodDef dictitems_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {"__reversed__", (PyCFunction)dictitems_reversed, METH_NOARGS, reversed_items_doc}, {NULL, NULL} /* sentinel */ }; static PyObject * dictitems_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterItem_Type); } /*** dict_values ***/ static PySequenceMethods dictvalues_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)0, /* sq_contains */ }; static PyObject* dictvalues_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_values_doc, "Return a reverse iterator over the dict values."); static PyMethodDef dictvalues_methods[] = { {"__reversed__", (PyCFunction)dictvalues_reversed, METH_NOARGS, reversed_values_doc}, {NULL, NULL} /* sentinel */ }; static PyObject * dictvalues_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterValue_Type); } python-frozendict/src/frozendict/c_src/3_9/cpython_src/Objects/dictobject_original.c000066400000000000000000004342261470250572200312720ustar00rootroot00000000000000/* Dictionary object implementation using a hash table */ /* The distribution includes a separate file, Objects/dictnotes.txt, describing explorations into dictionary design and optimization. It covers typical dictionary use patterns, the parameters for tuning dictionaries, and several ideas for possible optimizations. */ /* PyDictKeysObject This implements the dictionary's hashtable. As of Python 3.6, this is compact and ordered. Basic idea is described here: * https://mail.python.org/pipermail/python-dev/2012-December/123028.html * https://morepypy.blogspot.com/2015/01/faster-more-memory-efficient-and-more.html layout: +---------------+ | dk_refcnt | | dk_size | | dk_lookup | | dk_usable | | dk_nentries | +---------------+ | dk_indices | | | +---------------+ | dk_entries | | | +---------------+ dk_indices is actual hashtable. It holds index in entries, or DKIX_EMPTY(-1) or DKIX_DUMMY(-2). Size of indices is dk_size. Type of each index in indices is vary on dk_size: * int8 for dk_size <= 128 * int16 for 256 <= dk_size <= 2**15 * int32 for 2**16 <= dk_size <= 2**31 * int64 for 2**32 <= dk_size dk_entries is array of PyDictKeyEntry. Its size is USABLE_FRACTION(dk_size). DK_ENTRIES(dk) can be used to get pointer to entries. NOTE: Since negative value is used for DKIX_EMPTY and DKIX_DUMMY, type of dk_indices entry is signed integer and int16 is used for table which dk_size == 256. */ /* The DictObject can be in one of two forms. Either: A combined table: ma_values == NULL, dk_refcnt == 1. Values are stored in the me_value field of the PyDictKeysObject. Or: A split table: ma_values != NULL, dk_refcnt >= 1 Values are stored in the ma_values array. Only string (unicode) keys are allowed. All dicts sharing same key must have same insertion order. There are four kinds of slots in the table (slot is index, and DK_ENTRIES(keys)[index] if index >= 0): 1. Unused. index == DKIX_EMPTY Does not hold an active (key, value) pair now and never did. Unused can transition to Active upon key insertion. This is each slot's initial state. 2. Active. index >= 0, me_key != NULL and me_value != NULL Holds an active (key, value) pair. Active can transition to Dummy or Pending upon key deletion (for combined and split tables respectively). This is the only case in which me_value != NULL. 3. Dummy. index == DKIX_DUMMY (combined only) Previously held an active (key, value) pair, but that was deleted and an active pair has not yet overwritten the slot. Dummy can transition to Active upon key insertion. Dummy slots cannot be made Unused again else the probe sequence in case of collision would have no way to know they were once active. 4. Pending. index >= 0, key != NULL, and value == NULL (split only) Not yet inserted in split-table. */ /* Preserving insertion order It's simple for combined table. Since dk_entries is mostly append only, we can get insertion order by just iterating dk_entries. One exception is .popitem(). It removes last item in dk_entries and decrement dk_nentries to achieve amortized O(1). Since there are DKIX_DUMMY remains in dk_indices, we can't increment dk_usable even though dk_nentries is decremented. In split table, inserting into pending entry is allowed only for dk_entries[ix] where ix == mp->ma_used. Inserting into other index and deleting item cause converting the dict to the combined table. */ /* PyDict_MINSIZE is the starting size for any new dict. * 8 allows dicts with no more than 5 active entries; experiments suggested * this suffices for the majority of dicts (consisting mostly of usually-small * dicts created to pass keyword arguments). * Making this 8, rather than 4 reduces the number of resizes for most * dictionaries, without any significant extra memory use. */ #define PyDict_MINSIZE 8 #include "Python.h" #include "pycore_gc.h" // _PyObject_GC_IS_TRACKED() #include "pycore_object.h" #include "pycore_pystate.h" // _PyThreadState_GET() #include "dict-common.h" #include "stringlib/eq.h" // unicode_eq() /*[clinic input] class dict "PyDictObject *" "&PyDict_Type" [clinic start generated code]*/ /*[clinic end generated code: output=da39a3ee5e6b4b0d input=f157a5a0ce9589d6]*/ /* To ensure the lookup algorithm terminates, there must be at least one Unused slot (NULL key) in the table. To avoid slowing down lookups on a near-full table, we resize the table when it's USABLE_FRACTION (currently two-thirds) full. */ #define PERTURB_SHIFT 5 /* Major subtleties ahead: Most hash schemes depend on having a "good" hash function, in the sense of simulating randomness. Python doesn't: its most important hash functions (for ints) are very regular in common cases: >>>[hash(i) for i in range(4)] [0, 1, 2, 3] This isn't necessarily bad! To the contrary, in a table of size 2**i, taking the low-order i bits as the initial table index is extremely fast, and there are no collisions at all for dicts indexed by a contiguous range of ints. So this gives better-than-random behavior in common cases, and that's very desirable. OTOH, when collisions occur, the tendency to fill contiguous slices of the hash table makes a good collision resolution strategy crucial. Taking only the last i bits of the hash code is also vulnerable: for example, consider the list [i << 16 for i in range(20000)] as a set of keys. Since ints are their own hash codes, and this fits in a dict of size 2**15, the last 15 bits of every hash code are all 0: they *all* map to the same table index. But catering to unusual cases should not slow the usual ones, so we just take the last i bits anyway. It's up to collision resolution to do the rest. If we *usually* find the key we're looking for on the first try (and, it turns out, we usually do -- the table load factor is kept under 2/3, so the odds are solidly in our favor), then it makes best sense to keep the initial index computation dirt cheap. The first half of collision resolution is to visit table indices via this recurrence: j = ((5*j) + 1) mod 2**i For any initial j in range(2**i), repeating that 2**i times generates each int in range(2**i) exactly once (see any text on random-number generation for proof). By itself, this doesn't help much: like linear probing (setting j += 1, or j -= 1, on each loop trip), it scans the table entries in a fixed order. This would be bad, except that's not the only thing we do, and it's actually *good* in the common cases where hash keys are consecutive. In an example that's really too small to make this entirely clear, for a table of size 2**3 the order of indices is: 0 -> 1 -> 6 -> 7 -> 4 -> 5 -> 2 -> 3 -> 0 [and here it's repeating] If two things come in at index 5, the first place we look after is index 2, not 6, so if another comes in at index 6 the collision at 5 didn't hurt it. Linear probing is deadly in this case because there the fixed probe order is the *same* as the order consecutive keys are likely to arrive. But it's extremely unlikely hash codes will follow a 5*j+1 recurrence by accident, and certain that consecutive hash codes do not. The other half of the strategy is to get the other bits of the hash code into play. This is done by initializing a (unsigned) vrbl "perturb" to the full hash code, and changing the recurrence to: perturb >>= PERTURB_SHIFT; j = (5*j) + 1 + perturb; use j % 2**i as the next table index; Now the probe sequence depends (eventually) on every bit in the hash code, and the pseudo-scrambling property of recurring on 5*j+1 is more valuable, because it quickly magnifies small differences in the bits that didn't affect the initial index. Note that because perturb is unsigned, if the recurrence is executed often enough perturb eventually becomes and remains 0. At that point (very rarely reached) the recurrence is on (just) 5*j+1 again, and that's certain to find an empty slot eventually (since it generates every int in range(2**i), and we make sure there's always at least one empty slot). Selecting a good value for PERTURB_SHIFT is a balancing act. You want it small so that the high bits of the hash code continue to affect the probe sequence across iterations; but you want it large so that in really bad cases the high-order hash bits have an effect on early iterations. 5 was "the best" in minimizing total collisions across experiments Tim Peters ran (on both normal and pathological cases), but 4 and 6 weren't significantly worse. Historical: Reimer Behrends contributed the idea of using a polynomial-based approach, using repeated multiplication by x in GF(2**n) where an irreducible polynomial for each table size was chosen such that x was a primitive root. Christian Tismer later extended that to use division by x instead, as an efficient way to get the high bits of the hash code into play. This scheme also gave excellent collision statistics, but was more expensive: two if-tests were required inside the loop; computing "the next" index took about the same number of operations but without as much potential parallelism (e.g., computing 5*j can go on at the same time as computing 1+perturb in the above, and then shifting perturb can be done while the table index is being masked); and the PyDictObject struct required a member to hold the table's polynomial. In Tim's experiments the current scheme ran faster, produced equally good collision statistics, needed less code & used less memory. */ /* forward declarations */ static Py_ssize_t lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static Py_ssize_t lookdict_unicode(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static Py_ssize_t lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static Py_ssize_t lookdict_split(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr); static int dictresize(PyDictObject *mp, Py_ssize_t minused); static PyObject* dict_iter(PyDictObject *dict); /*Global counter used to set ma_version_tag field of dictionary. * It is incremented each time that a dictionary is created and each * time that a dictionary is modified. */ static uint64_t pydict_global_version = 0; #define DICT_NEXT_VERSION() (++pydict_global_version) /* Dictionary reuse scheme to save calls to malloc and free */ #ifndef PyDict_MAXFREELIST #define PyDict_MAXFREELIST 80 #endif #if PyDict_MAXFREELIST > 0 static PyDictObject *free_list[PyDict_MAXFREELIST]; static int numfree = 0; static PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST]; static int numfreekeys = 0; #endif #include "clinic/dictobject.c.h" void _PyDict_ClearFreeList(void) { #if PyDict_MAXFREELIST > 0 while (numfree) { PyDictObject *op = free_list[--numfree]; assert(PyDict_CheckExact(op)); PyObject_GC_Del(op); } while (numfreekeys) { PyObject_FREE(keys_free_list[--numfreekeys]); } #endif } /* Print summary info about the state of the optimized allocator */ void _PyDict_DebugMallocStats(FILE *out) { #if PyDict_MAXFREELIST > 0 _PyDebugAllocatorStats(out, "free PyDictObject", numfree, sizeof(PyDictObject)); #endif } void _PyDict_Fini(void) { _PyDict_ClearFreeList(); } #define DK_SIZE(dk) ((dk)->dk_size) #if SIZEOF_VOID_P > 4 #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : DK_SIZE(dk) <= 0xffffffff ? \ 4 : sizeof(int64_t)) #else #define DK_IXSIZE(dk) \ (DK_SIZE(dk) <= 0xff ? \ 1 : DK_SIZE(dk) <= 0xffff ? \ 2 : sizeof(int32_t)) #endif #define DK_ENTRIES(dk) \ ((PyDictKeyEntry*)(&((int8_t*)((dk)->dk_indices))[DK_SIZE(dk) * DK_IXSIZE(dk)])) #define DK_MASK(dk) (((dk)->dk_size)-1) #define IS_POWER_OF_2(x) (((x) & (x-1)) == 0) static void free_keys_object(PyDictKeysObject *keys); static inline void dictkeys_incref(PyDictKeysObject *dk) { #ifdef Py_REF_DEBUG _Py_RefTotal++; #endif dk->dk_refcnt++; } static inline void dictkeys_decref(PyDictKeysObject *dk) { assert(dk->dk_refcnt > 0); #ifdef Py_REF_DEBUG _Py_RefTotal--; #endif if (--dk->dk_refcnt == 0) { free_keys_object(dk); } } /* lookup indices. returns DKIX_EMPTY, DKIX_DUMMY, or ix >=0 */ static inline Py_ssize_t dictkeys_get_index(const PyDictKeysObject *keys, Py_ssize_t i) { Py_ssize_t s = DK_SIZE(keys); Py_ssize_t ix; if (s <= 0xff) { const int8_t *indices = (const int8_t*)(keys->dk_indices); ix = indices[i]; } else if (s <= 0xffff) { const int16_t *indices = (const int16_t*)(keys->dk_indices); ix = indices[i]; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { const int64_t *indices = (const int64_t*)(keys->dk_indices); ix = indices[i]; } #endif else { const int32_t *indices = (const int32_t*)(keys->dk_indices); ix = indices[i]; } assert(ix >= DKIX_DUMMY); return ix; } /* write to indices. */ static inline void dictkeys_set_index(PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix) { Py_ssize_t s = DK_SIZE(keys); assert(ix >= DKIX_DUMMY); if (s <= 0xff) { int8_t *indices = (int8_t*)(keys->dk_indices); assert(ix <= 0x7f); indices[i] = (char)ix; } else if (s <= 0xffff) { int16_t *indices = (int16_t*)(keys->dk_indices); assert(ix <= 0x7fff); indices[i] = (int16_t)ix; } #if SIZEOF_VOID_P > 4 else if (s > 0xffffffff) { int64_t *indices = (int64_t*)(keys->dk_indices); indices[i] = ix; } #endif else { int32_t *indices = (int32_t*)(keys->dk_indices); assert(ix <= 0x7fffffff); indices[i] = (int32_t)ix; } } /* USABLE_FRACTION is the maximum dictionary load. * Increasing this ratio makes dictionaries more dense resulting in more * collisions. Decreasing it improves sparseness at the expense of spreading * indices over more cache lines and at the cost of total memory consumed. * * USABLE_FRACTION must obey the following: * (0 < USABLE_FRACTION(n) < n) for all n >= 2 * * USABLE_FRACTION should be quick to calculate. * Fractions around 1/2 to 2/3 seem to work well in practice. */ #define USABLE_FRACTION(n) (((n) << 1)/3) /* ESTIMATE_SIZE is reverse function of USABLE_FRACTION. * This can be used to reserve enough size to insert n entries without * resizing. */ #define ESTIMATE_SIZE(n) (((n)*3+1) >> 1) /* Alternative fraction that is otherwise close enough to 2n/3 to make * little difference. 8 * 2/3 == 8 * 5/8 == 5. 16 * 2/3 == 16 * 5/8 == 10. * 32 * 2/3 = 21, 32 * 5/8 = 20. * Its advantage is that it is faster to compute on machines with slow division. * #define USABLE_FRACTION(n) (((n) >> 1) + ((n) >> 2) - ((n) >> 3)) */ /* GROWTH_RATE. Growth rate upon hitting maximum load. * Currently set to used*3. * This means that dicts double in size when growing without deletions, * but have more head room when the number of deletions is on a par with the * number of insertions. See also bpo-17563 and bpo-33205. * * GROWTH_RATE was set to used*4 up to version 3.2. * GROWTH_RATE was set to used*2 in version 3.3.0 * GROWTH_RATE was set to used*2 + capacity/2 in 3.4.0-3.6.0. */ #define GROWTH_RATE(d) ((d)->ma_used*3) #define ENSURE_ALLOWS_DELETIONS(d) \ if ((d)->ma_keys->dk_lookup == lookdict_unicode_nodummy) { \ (d)->ma_keys->dk_lookup = lookdict_unicode; \ } /* This immutable, empty PyDictKeysObject is used for PyDict_Clear() * (which cannot fail and thus can do no allocation). */ static PyDictKeysObject empty_keys_struct = { 1, /* dk_refcnt */ 1, /* dk_size */ lookdict_split, /* dk_lookup */ 0, /* dk_usable (immutable) */ 0, /* dk_nentries */ {DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY, DKIX_EMPTY}, /* dk_indices */ }; static PyObject *empty_values[1] = { NULL }; #define Py_EMPTY_KEYS &empty_keys_struct /* Uncomment to check the dict content in _PyDict_CheckConsistency() */ /* #define DEBUG_PYDICT */ #ifdef DEBUG_PYDICT # define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 1)) #else # define ASSERT_CONSISTENT(op) assert(_PyDict_CheckConsistency((PyObject *)(op), 0)) #endif int _PyDict_CheckConsistency(PyObject *op, int check_content) { #define CHECK(expr) \ do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0) assert(op != NULL); CHECK(PyDict_Check(op)); PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; int splitted = _PyDict_HasSplitTable(mp); Py_ssize_t usable = USABLE_FRACTION(keys->dk_size); CHECK(0 <= mp->ma_used && mp->ma_used <= usable); CHECK(IS_POWER_OF_2(keys->dk_size)); CHECK(0 <= keys->dk_usable && keys->dk_usable <= usable); CHECK(0 <= keys->dk_nentries && keys->dk_nentries <= usable); CHECK(keys->dk_usable + keys->dk_nentries <= usable); if (!splitted) { /* combined table */ CHECK(keys->dk_refcnt == 1); } if (check_content) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i; for (i=0; i < keys->dk_size; i++) { Py_ssize_t ix = dictkeys_get_index(keys, i); CHECK(DKIX_DUMMY <= ix && ix <= usable); } for (i=0; i < usable; i++) { PyDictKeyEntry *entry = &entries[i]; PyObject *key = entry->me_key; if (key != NULL) { if (PyUnicode_CheckExact(key)) { Py_hash_t hash = ((PyASCIIObject *)key)->hash; CHECK(hash != -1); CHECK(entry->me_hash == hash); } else { /* test_dict fails if PyObject_Hash() is called again */ CHECK(entry->me_hash != -1); } if (!splitted) { CHECK(entry->me_value != NULL); } } if (splitted) { CHECK(entry->me_value == NULL); } } if (splitted) { /* splitted table */ for (i=0; i < mp->ma_used; i++) { CHECK(mp->ma_values[i] != NULL); } } } return 1; #undef CHECK } static PyDictKeysObject *new_keys_object(Py_ssize_t size) { PyDictKeysObject *dk; Py_ssize_t es, usable; assert(size >= PyDict_MINSIZE); assert(IS_POWER_OF_2(size)); usable = USABLE_FRACTION(size); if (size <= 0xff) { es = 1; } else if (size <= 0xffff) { es = 2; } #if SIZEOF_VOID_P > 4 else if (size <= 0xffffffff) { es = 4; } #endif else { es = sizeof(Py_ssize_t); } #if PyDict_MAXFREELIST > 0 if (size == PyDict_MINSIZE && numfreekeys > 0) { dk = keys_free_list[--numfreekeys]; } else #endif { dk = PyObject_MALLOC(sizeof(PyDictKeysObject) + es * size + sizeof(PyDictKeyEntry) * usable); if (dk == NULL) { PyErr_NoMemory(); return NULL; } } #ifdef Py_REF_DEBUG _Py_RefTotal++; #endif dk->dk_refcnt = 1; dk->dk_size = size; dk->dk_usable = usable; dk->dk_lookup = lookdict_unicode_nodummy; dk->dk_nentries = 0; memset(&dk->dk_indices[0], 0xff, es * size); memset(DK_ENTRIES(dk), 0, sizeof(PyDictKeyEntry) * usable); return dk; } static void free_keys_object(PyDictKeysObject *keys) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n; for (i = 0, n = keys->dk_nentries; i < n; i++) { Py_XDECREF(entries[i].me_key); Py_XDECREF(entries[i].me_value); } #if PyDict_MAXFREELIST > 0 if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) { keys_free_list[numfreekeys++] = keys; return; } #endif PyObject_FREE(keys); } #define new_values(size) PyMem_NEW(PyObject *, size) #define free_values(values) PyMem_FREE(values) /* Consumes a reference to the keys object */ static PyObject * new_dict(PyDictKeysObject *keys, PyObject **values) { PyDictObject *mp; assert(keys != NULL); #if PyDict_MAXFREELIST > 0 if (numfree) { mp = free_list[--numfree]; assert (mp != NULL); assert (Py_IS_TYPE(mp, &PyDict_Type)); _Py_NewReference((PyObject *)mp); } else #endif { mp = PyObject_GC_New(PyDictObject, &PyDict_Type); if (mp == NULL) { dictkeys_decref(keys); if (values != empty_values) { free_values(values); } return NULL; } } mp->ma_keys = keys; mp->ma_values = values; mp->ma_used = 0; mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(mp); return (PyObject *)mp; } /* Consumes a reference to the keys object */ static PyObject * new_dict_with_shared_keys(PyDictKeysObject *keys) { PyObject **values; Py_ssize_t i, size; size = USABLE_FRACTION(DK_SIZE(keys)); values = new_values(size); if (values == NULL) { dictkeys_decref(keys); return PyErr_NoMemory(); } for (i = 0; i < size; i++) { values[i] = NULL; } return new_dict(keys, values); } static PyObject * clone_combined_dict(PyDictObject *orig) { assert(PyDict_CheckExact(orig)); assert(orig->ma_values == NULL); assert(orig->ma_keys->dk_refcnt == 1); Py_ssize_t keys_size = _PyDict_KeysSize(orig->ma_keys); PyDictKeysObject *keys = PyObject_Malloc(keys_size); if (keys == NULL) { PyErr_NoMemory(); return NULL; } memcpy(keys, orig->ma_keys, keys_size); /* After copying key/value pairs, we need to incref all keys and values and they are about to be co-owned by a new dict object. */ PyDictKeyEntry *ep0 = DK_ENTRIES(keys); Py_ssize_t n = keys->dk_nentries; for (Py_ssize_t i = 0; i < n; i++) { PyDictKeyEntry *entry = &ep0[i]; PyObject *value = entry->me_value; if (value != NULL) { Py_INCREF(value); Py_INCREF(entry->me_key); } } PyDictObject *new = (PyDictObject *)new_dict(keys, NULL); if (new == NULL) { /* In case of an error, `new_dict()` takes care of cleaning up `keys`. */ return NULL; } new->ma_used = orig->ma_used; ASSERT_CONSISTENT(new); if (_PyObject_GC_IS_TRACKED(orig)) { /* Maintain tracking. */ _PyObject_GC_TRACK(new); } /* Since we copied the keys table we now have an extra reference in the system. Manually call increment _Py_RefTotal to signal that we have it now; calling dictkeys_incref would be an error as keys->dk_refcnt is already set to 1 (after memcpy). */ #ifdef Py_REF_DEBUG _Py_RefTotal++; #endif return (PyObject *)new; } PyObject * PyDict_New(void) { dictkeys_incref(Py_EMPTY_KEYS); return new_dict(Py_EMPTY_KEYS, empty_values); } /* Search index of hash table from offset of entry table */ static Py_ssize_t lookdict_index(PyDictKeysObject *k, Py_hash_t hash, Py_ssize_t index) { size_t mask = DK_MASK(k); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(k, i); if (ix == index) { return i; } if (ix == DKIX_EMPTY) { return DKIX_EMPTY; } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } /* The basic lookup function used by all operations. This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4. Open addressing is preferred over chaining since the link overhead for chaining would be substantial (100% with typical malloc overhead). The initial probe index is computed as hash mod the table size. Subsequent probe indices are computed as explained earlier. All arithmetic on hash should ignore overflow. The details in this version are due to Tim Peters, building on many past contributions by Reimer Behrends, Jyrki Alakuijala, Vladimir Marangozov and Christian Tismer. lookdict() is general-purpose, and may return DKIX_ERROR if (and only if) a comparison raises an exception. lookdict_unicode() below is specialized to string keys, comparison of which can never raise an exception; that function can never return DKIX_ERROR when key is string. Otherwise, it falls back to lookdict(). lookdict_unicode_nodummy is further specialized for string keys that cannot be the value. For both, when the key isn't found a DKIX_EMPTY is returned. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { size_t i, mask, perturb; PyDictKeysObject *dk; PyDictKeyEntry *ep0; top: dk = mp->ma_keys; ep0 = DK_ENTRIES(dk); mask = DK_MASK(dk); perturb = hash; i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(dk, i); if (ix == DKIX_EMPTY) { *value_addr = NULL; return ix; } if (ix >= 0) { PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); if (ep->me_key == key) { *value_addr = ep->me_value; return ix; } if (ep->me_hash == hash) { PyObject *startkey = ep->me_key; Py_INCREF(startkey); int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ); Py_DECREF(startkey); if (cmp < 0) { *value_addr = NULL; return DKIX_ERROR; } if (dk == mp->ma_keys && ep->me_key == startkey) { if (cmp > 0) { *value_addr = ep->me_value; return ix; } } else { /* The dict was mutated, restart */ goto top; } } } perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; } Py_UNREACHABLE(); } /* Specialized version for string-only keys */ static Py_ssize_t _Py_HOT_FUNCTION lookdict_unicode(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { assert(mp->ma_values == NULL); /* Make sure this function doesn't have to handle non-unicode keys, including subclasses of str; e.g., one reason to subclass unicodes is to override __eq__, and for speed we don't cater to that here. */ if (!PyUnicode_CheckExact(key)) { mp->ma_keys->dk_lookup = lookdict; return lookdict(mp, key, hash, value_addr); } PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); size_t mask = DK_MASK(mp->ma_keys); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i); if (ix == DKIX_EMPTY) { *value_addr = NULL; return DKIX_EMPTY; } if (ix >= 0) { PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { *value_addr = ep->me_value; return ix; } } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } /* Faster version of lookdict_unicode when it is known that no keys * will be present. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict_unicode_nodummy(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { assert(mp->ma_values == NULL); /* Make sure this function doesn't have to handle non-unicode keys, including subclasses of str; e.g., one reason to subclass unicodes is to override __eq__, and for speed we don't cater to that here. */ if (!PyUnicode_CheckExact(key)) { mp->ma_keys->dk_lookup = lookdict; return lookdict(mp, key, hash, value_addr); } PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); size_t mask = DK_MASK(mp->ma_keys); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i); assert (ix != DKIX_DUMMY); if (ix == DKIX_EMPTY) { *value_addr = NULL; return DKIX_EMPTY; } PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { *value_addr = ep->me_value; return ix; } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } /* Version of lookdict for split tables. * All split tables and only split tables use this lookup function. * Split tables only contain unicode keys and no dummy keys, * so algorithm is the same as lookdict_unicode_nodummy. */ static Py_ssize_t _Py_HOT_FUNCTION lookdict_split(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr) { /* mp must split table */ assert(mp->ma_values != NULL); if (!PyUnicode_CheckExact(key)) { Py_ssize_t ix = lookdict(mp, key, hash, value_addr); if (ix >= 0) { *value_addr = mp->ma_values[ix]; } return ix; } PyDictKeyEntry *ep0 = DK_ENTRIES(mp->ma_keys); size_t mask = DK_MASK(mp->ma_keys); size_t perturb = (size_t)hash; size_t i = (size_t)hash & mask; for (;;) { Py_ssize_t ix = dictkeys_get_index(mp->ma_keys, i); assert (ix != DKIX_DUMMY); if (ix == DKIX_EMPTY) { *value_addr = NULL; return DKIX_EMPTY; } PyDictKeyEntry *ep = &ep0[ix]; assert(ep->me_key != NULL); assert(PyUnicode_CheckExact(ep->me_key)); if (ep->me_key == key || (ep->me_hash == hash && unicode_eq(ep->me_key, key))) { *value_addr = mp->ma_values[ix]; return ix; } perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } Py_UNREACHABLE(); } int _PyDict_HasOnlyStringKeys(PyObject *dict) { Py_ssize_t pos = 0; PyObject *key, *value; assert(PyDict_Check(dict)); /* Shortcut */ if (((PyDictObject *)dict)->ma_keys->dk_lookup != lookdict) return 1; while (PyDict_Next(dict, &pos, &key, &value)) if (!PyUnicode_Check(key)) return 0; return 1; } #define MAINTAIN_TRACKING(mp, key, value) \ do { \ if (!_PyObject_GC_IS_TRACKED(mp)) { \ if (_PyObject_GC_MAY_BE_TRACKED(key) || \ _PyObject_GC_MAY_BE_TRACKED(value)) { \ _PyObject_GC_TRACK(mp); \ } \ } \ } while(0) void _PyDict_MaybeUntrack(PyObject *op) { PyDictObject *mp; PyObject *value; Py_ssize_t i, numentries; PyDictKeyEntry *ep0; if (!PyDict_CheckExact(op) || !_PyObject_GC_IS_TRACKED(op)) return; mp = (PyDictObject *) op; ep0 = DK_ENTRIES(mp->ma_keys); numentries = mp->ma_keys->dk_nentries; if (_PyDict_HasSplitTable(mp)) { for (i = 0; i < numentries; i++) { if ((value = mp->ma_values[i]) == NULL) continue; if (_PyObject_GC_MAY_BE_TRACKED(value)) { assert(!_PyObject_GC_MAY_BE_TRACKED(ep0[i].me_key)); return; } } } else { for (i = 0; i < numentries; i++) { if ((value = ep0[i].me_value) == NULL) continue; if (_PyObject_GC_MAY_BE_TRACKED(value) || _PyObject_GC_MAY_BE_TRACKED(ep0[i].me_key)) return; } } _PyObject_GC_UNTRACK(op); } /* Internal function to find slot for an item from its hash when it is known that the key is not present in the dict. The dict must be combined. */ static Py_ssize_t find_empty_slot(PyDictKeysObject *keys, Py_hash_t hash) { assert(keys != NULL); const size_t mask = DK_MASK(keys); size_t i = hash & mask; Py_ssize_t ix = dictkeys_get_index(keys, i); for (size_t perturb = hash; ix >= 0;) { perturb >>= PERTURB_SHIFT; i = (i*5 + perturb + 1) & mask; ix = dictkeys_get_index(keys, i); } return i; } static int insertion_resize(PyDictObject *mp) { return dictresize(mp, GROWTH_RATE(mp)); } /* Internal routine to insert a new item into the table. Used both by the internal resize routine and by the public insert routine. Returns -1 if an error occurred, or 0 on success. */ static int insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) { PyObject *old_value; PyDictKeyEntry *ep; Py_INCREF(key); Py_INCREF(value); if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) { if (insertion_resize(mp) < 0) goto Fail; } Py_ssize_t ix = mp->ma_keys->dk_lookup(mp, key, hash, &old_value); if (ix == DKIX_ERROR) goto Fail; assert(PyUnicode_CheckExact(key) || mp->ma_keys->dk_lookup == lookdict); MAINTAIN_TRACKING(mp, key, value); /* When insertion order is different from shared key, we can't share * the key anymore. Convert this instance to combine table. */ if (_PyDict_HasSplitTable(mp) && ((ix >= 0 && old_value == NULL && mp->ma_used != ix) || (ix == DKIX_EMPTY && mp->ma_used != mp->ma_keys->dk_nentries))) { if (insertion_resize(mp) < 0) goto Fail; ix = DKIX_EMPTY; } if (ix == DKIX_EMPTY) { /* Insert into new slot. */ assert(old_value == NULL); if (mp->ma_keys->dk_usable <= 0) { /* Need to resize. */ if (insertion_resize(mp) < 0) goto Fail; } Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash); ep = &DK_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries]; dictkeys_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries); ep->me_key = key; ep->me_hash = hash; if (mp->ma_values) { assert (mp->ma_values[mp->ma_keys->dk_nentries] == NULL); mp->ma_values[mp->ma_keys->dk_nentries] = value; } else { ep->me_value = value; } mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); mp->ma_keys->dk_usable--; mp->ma_keys->dk_nentries++; assert(mp->ma_keys->dk_usable >= 0); ASSERT_CONSISTENT(mp); return 0; } if (old_value != value) { if (_PyDict_HasSplitTable(mp)) { mp->ma_values[ix] = value; if (old_value == NULL) { /* pending state */ assert(ix == mp->ma_used); mp->ma_used++; } } else { assert(old_value != NULL); DK_ENTRIES(mp->ma_keys)[ix].me_value = value; } mp->ma_version_tag = DICT_NEXT_VERSION(); } Py_XDECREF(old_value); /* which **CAN** re-enter (see issue #22653) */ ASSERT_CONSISTENT(mp); Py_DECREF(key); return 0; Fail: Py_DECREF(value); Py_DECREF(key); return -1; } // Same to insertdict but specialized for ma_keys = Py_EMPTY_KEYS. static int insert_to_emptydict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) { assert(mp->ma_keys == Py_EMPTY_KEYS); PyDictKeysObject *newkeys = new_keys_object(PyDict_MINSIZE); if (newkeys == NULL) { return -1; } if (!PyUnicode_CheckExact(key)) { newkeys->dk_lookup = lookdict; } dictkeys_decref(Py_EMPTY_KEYS); mp->ma_keys = newkeys; mp->ma_values = NULL; Py_INCREF(key); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); size_t hashpos = (size_t)hash & (PyDict_MINSIZE-1); PyDictKeyEntry *ep = DK_ENTRIES(mp->ma_keys); dictkeys_set_index(mp->ma_keys, hashpos, 0); ep->me_key = key; ep->me_hash = hash; ep->me_value = value; mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); mp->ma_keys->dk_usable--; mp->ma_keys->dk_nentries++; return 0; } /* Internal routine used by dictresize() to build a hashtable of entries. */ static void build_indices(PyDictKeysObject *keys, PyDictKeyEntry *ep, Py_ssize_t n) { size_t mask = (size_t)DK_SIZE(keys) - 1; for (Py_ssize_t ix = 0; ix != n; ix++, ep++) { Py_hash_t hash = ep->me_hash; size_t i = hash & mask; for (size_t perturb = hash; dictkeys_get_index(keys, i) != DKIX_EMPTY;) { perturb >>= PERTURB_SHIFT; i = mask & (i*5 + perturb + 1); } dictkeys_set_index(keys, i, ix); } } /* Restructure the table by allocating a new table and reinserting all items again. When entries have been deleted, the new table may actually be smaller than the old one. If a table is split (its keys and hashes are shared, its values are not), then the values are temporarily copied into the table, it is resized as a combined table, then the me_value slots in the old table are NULLed out. After resizing a table is always combined, but can be resplit by make_keys_shared(). */ static int dictresize(PyDictObject *mp, Py_ssize_t minsize) { Py_ssize_t newsize, numentries; PyDictKeysObject *oldkeys; PyObject **oldvalues; PyDictKeyEntry *oldentries, *newentries; /* Find the smallest table size > minused. */ for (newsize = PyDict_MINSIZE; newsize < minsize && newsize > 0; newsize <<= 1) ; if (newsize <= 0) { PyErr_NoMemory(); return -1; } oldkeys = mp->ma_keys; /* NOTE: Current odict checks mp->ma_keys to detect resize happen. * So we can't reuse oldkeys even if oldkeys->dk_size == newsize. * TODO: Try reusing oldkeys when reimplement odict. */ /* Allocate a new table. */ mp->ma_keys = new_keys_object(newsize); if (mp->ma_keys == NULL) { mp->ma_keys = oldkeys; return -1; } // New table must be large enough. assert(mp->ma_keys->dk_usable >= mp->ma_used); if (oldkeys->dk_lookup == lookdict) mp->ma_keys->dk_lookup = lookdict; numentries = mp->ma_used; oldentries = DK_ENTRIES(oldkeys); newentries = DK_ENTRIES(mp->ma_keys); oldvalues = mp->ma_values; if (oldvalues != NULL) { /* Convert split table into new combined table. * We must incref keys; we can transfer values. * Note that values of split table is always dense. */ for (Py_ssize_t i = 0; i < numentries; i++) { assert(oldvalues[i] != NULL); PyDictKeyEntry *ep = &oldentries[i]; PyObject *key = ep->me_key; Py_INCREF(key); newentries[i].me_key = key; newentries[i].me_hash = ep->me_hash; newentries[i].me_value = oldvalues[i]; } dictkeys_decref(oldkeys); mp->ma_values = NULL; if (oldvalues != empty_values) { free_values(oldvalues); } } else { // combined table. if (oldkeys->dk_nentries == numentries) { memcpy(newentries, oldentries, numentries * sizeof(PyDictKeyEntry)); } else { PyDictKeyEntry *ep = oldentries; for (Py_ssize_t i = 0; i < numentries; i++) { while (ep->me_value == NULL) ep++; newentries[i] = *ep++; } } assert(oldkeys->dk_lookup != lookdict_split); assert(oldkeys->dk_refcnt == 1); #ifdef Py_REF_DEBUG _Py_RefTotal--; #endif #if PyDict_MAXFREELIST > 0 if (oldkeys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) { keys_free_list[numfreekeys++] = oldkeys; } else #endif { PyObject_FREE(oldkeys); } } build_indices(mp->ma_keys, newentries, numentries); mp->ma_keys->dk_usable -= numentries; mp->ma_keys->dk_nentries = numentries; return 0; } /* Returns NULL if unable to split table. * A NULL return does not necessarily indicate an error */ static PyDictKeysObject * make_keys_shared(PyObject *op) { Py_ssize_t i; Py_ssize_t size; PyDictObject *mp = (PyDictObject *)op; if (!PyDict_CheckExact(op)) return NULL; if (!_PyDict_HasSplitTable(mp)) { PyDictKeyEntry *ep0; PyObject **values; assert(mp->ma_keys->dk_refcnt == 1); if (mp->ma_keys->dk_lookup == lookdict) { return NULL; } else if (mp->ma_keys->dk_lookup == lookdict_unicode) { /* Remove dummy keys */ if (dictresize(mp, DK_SIZE(mp->ma_keys))) return NULL; } assert(mp->ma_keys->dk_lookup == lookdict_unicode_nodummy); /* Copy values into a new array */ ep0 = DK_ENTRIES(mp->ma_keys); size = USABLE_FRACTION(DK_SIZE(mp->ma_keys)); values = new_values(size); if (values == NULL) { PyErr_SetString(PyExc_MemoryError, "Not enough memory to allocate new values array"); return NULL; } for (i = 0; i < size; i++) { values[i] = ep0[i].me_value; ep0[i].me_value = NULL; } mp->ma_keys->dk_lookup = lookdict_split; mp->ma_values = values; } dictkeys_incref(mp->ma_keys); return mp->ma_keys; } PyObject * _PyDict_NewPresized(Py_ssize_t minused) { const Py_ssize_t max_presize = 128 * 1024; Py_ssize_t newsize; PyDictKeysObject *new_keys; if (minused <= USABLE_FRACTION(PyDict_MINSIZE)) { return PyDict_New(); } /* There are no strict guarantee that returned dict can contain minused * items without resize. So we create medium size dict instead of very * large dict or MemoryError. */ if (minused > USABLE_FRACTION(max_presize)) { newsize = max_presize; } else { Py_ssize_t minsize = ESTIMATE_SIZE(minused); newsize = PyDict_MINSIZE*2; while (newsize < minsize) { newsize <<= 1; } } assert(IS_POWER_OF_2(newsize)); new_keys = new_keys_object(newsize); if (new_keys == NULL) return NULL; return new_dict(new_keys, NULL); } /* Note that, for historical reasons, PyDict_GetItem() suppresses all errors * that may occur (originally dicts supported only string keys, and exceptions * weren't possible). So, while the original intent was that a NULL return * meant the key wasn't present, in reality it can mean that, or that an error * (suppressed) occurred while computing the key's hash, or that some error * (suppressed) occurred when comparing keys in the dict's internal probe * sequence. A nasty example of the latter is when a Python-coded comparison * function hits a stack-depth error, which can cause this to return NULL * even if the key is present. */ PyObject * PyDict_GetItem(PyObject *op, PyObject *key) { Py_hash_t hash; Py_ssize_t ix; PyDictObject *mp = (PyDictObject *)op; PyThreadState *tstate; PyObject *value; if (!PyDict_Check(op)) return NULL; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { PyErr_Clear(); return NULL; } } /* We can arrive here with a NULL tstate during initialization: try running "python -Wi" for an example related to string interning. Let's just hope that no exception occurs then... This must be _PyThreadState_GET() and not PyThreadState_Get() because the latter abort Python if tstate is NULL. */ tstate = _PyThreadState_GET(); if (tstate != NULL && tstate->curexc_type != NULL) { /* preserve the existing exception */ PyObject *err_type, *err_value, *err_tb; PyErr_Fetch(&err_type, &err_value, &err_tb); ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); /* ignore errors */ PyErr_Restore(err_type, err_value, err_tb); if (ix < 0) return NULL; } else { ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix < 0) { PyErr_Clear(); return NULL; } } return value; } /* Same as PyDict_GetItemWithError() but with hash supplied by caller. This returns NULL *with* an exception set if an exception occurred. It returns NULL *without* an exception set if the key wasn't present. */ PyObject * _PyDict_GetItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) { Py_ssize_t ix; PyDictObject *mp = (PyDictObject *)op; PyObject *value; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix < 0) { return NULL; } return value; } /* Variant of PyDict_GetItem() that doesn't suppress exceptions. This returns NULL *with* an exception set if an exception occurred. It returns NULL *without* an exception set if the key wasn't present. */ PyObject * PyDict_GetItemWithError(PyObject *op, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyDictObject*mp = (PyDictObject *)op; PyObject *value; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { return NULL; } } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix < 0) return NULL; return value; } PyObject * _PyDict_GetItemIdWithError(PyObject *dp, struct _Py_Identifier *key) { PyObject *kv; kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) return NULL; Py_hash_t hash = ((PyASCIIObject *) kv)->hash; assert (hash != -1); /* interned strings have their hash value initialised */ return _PyDict_GetItem_KnownHash(dp, kv, hash); } PyObject * _PyDict_GetItemStringWithError(PyObject *v, const char *key) { PyObject *kv, *rv; kv = PyUnicode_FromString(key); if (kv == NULL) { return NULL; } rv = PyDict_GetItemWithError(v, kv); Py_DECREF(kv); return rv; } /* Fast version of global value lookup (LOAD_GLOBAL). * Lookup in globals, then builtins. * * Raise an exception and return NULL if an error occurred (ex: computing the * key hash failed, key comparison failed, ...). Return NULL if the key doesn't * exist. Return the value if the key exists. */ PyObject * _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } /* namespace 1: globals */ ix = globals->ma_keys->dk_lookup(globals, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix != DKIX_EMPTY && value != NULL) return value; /* namespace 2: builtins */ ix = builtins->ma_keys->dk_lookup(builtins, key, hash, &value); if (ix < 0) return NULL; return value; } /* CAUTION: PyDict_SetItem() must guarantee that it won't resize the * dictionary if it's merely replacing the value for an existing key. * This means that it's safe to loop over a dictionary with PyDict_Next() * and occasionally replace a value -- but you can't insert new keys or * remove them. */ int PyDict_SetItem(PyObject *op, PyObject *key, PyObject *value) { PyDictObject *mp; Py_hash_t hash; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); assert(value); mp = (PyDictObject *)op; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; } if (mp->ma_keys == Py_EMPTY_KEYS) { return insert_to_emptydict(mp, key, hash, value); } /* insertdict() handles any resizing that might be necessary */ return insertdict(mp, key, hash, value); } int _PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value, Py_hash_t hash) { PyDictObject *mp; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); assert(value); assert(hash != -1); mp = (PyDictObject *)op; if (mp->ma_keys == Py_EMPTY_KEYS) { return insert_to_emptydict(mp, key, hash, value); } /* insertdict() handles any resizing that might be necessary */ return insertdict(mp, key, hash, value); } static int delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix, PyObject *old_value) { PyObject *old_key; PyDictKeyEntry *ep; Py_ssize_t hashpos = lookdict_index(mp->ma_keys, hash, ix); assert(hashpos >= 0); mp->ma_used--; mp->ma_version_tag = DICT_NEXT_VERSION(); ep = &DK_ENTRIES(mp->ma_keys)[ix]; dictkeys_set_index(mp->ma_keys, hashpos, DKIX_DUMMY); ENSURE_ALLOWS_DELETIONS(mp); old_key = ep->me_key; ep->me_key = NULL; ep->me_value = NULL; Py_DECREF(old_key); Py_DECREF(old_value); ASSERT_CONSISTENT(mp); return 0; } int PyDict_DelItem(PyObject *op, PyObject *key) { Py_hash_t hash; assert(key); if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; } return _PyDict_DelItem_KnownHash(op, key, hash); } int _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash) { Py_ssize_t ix; PyDictObject *mp; PyObject *old_value; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); assert(hash != -1); mp = (PyDictObject *)op; ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); if (ix == DKIX_ERROR) return -1; if (ix == DKIX_EMPTY || old_value == NULL) { _PyErr_SetKeyError(key); return -1; } // Split table doesn't allow deletion. Combine it. if (_PyDict_HasSplitTable(mp)) { if (dictresize(mp, DK_SIZE(mp->ma_keys))) { return -1; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); assert(ix >= 0); } return delitem_common(mp, hash, ix, old_value); } /* This function promises that the predicate -> deletion sequence is atomic * (i.e. protected by the GIL), assuming the predicate itself doesn't * release the GIL. */ int _PyDict_DelItemIf(PyObject *op, PyObject *key, int (*predicate)(PyObject *value)) { Py_ssize_t hashpos, ix; PyDictObject *mp; Py_hash_t hash; PyObject *old_value; int res; if (!PyDict_Check(op)) { PyErr_BadInternalCall(); return -1; } assert(key); hash = PyObject_Hash(key); if (hash == -1) return -1; mp = (PyDictObject *)op; ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); if (ix == DKIX_ERROR) return -1; if (ix == DKIX_EMPTY || old_value == NULL) { _PyErr_SetKeyError(key); return -1; } // Split table doesn't allow deletion. Combine it. if (_PyDict_HasSplitTable(mp)) { if (dictresize(mp, DK_SIZE(mp->ma_keys))) { return -1; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); assert(ix >= 0); } res = predicate(old_value); if (res == -1) return -1; hashpos = lookdict_index(mp->ma_keys, hash, ix); assert(hashpos >= 0); if (res > 0) return delitem_common(mp, hashpos, ix, old_value); else return 0; } void PyDict_Clear(PyObject *op) { PyDictObject *mp; PyDictKeysObject *oldkeys; PyObject **oldvalues; Py_ssize_t i, n; if (!PyDict_Check(op)) return; mp = ((PyDictObject *)op); oldkeys = mp->ma_keys; oldvalues = mp->ma_values; if (oldvalues == empty_values) return; /* Empty the dict... */ dictkeys_incref(Py_EMPTY_KEYS); mp->ma_keys = Py_EMPTY_KEYS; mp->ma_values = empty_values; mp->ma_used = 0; mp->ma_version_tag = DICT_NEXT_VERSION(); /* ...then clear the keys and values */ if (oldvalues != NULL) { n = oldkeys->dk_nentries; for (i = 0; i < n; i++) Py_CLEAR(oldvalues[i]); free_values(oldvalues); dictkeys_decref(oldkeys); } else { assert(oldkeys->dk_refcnt == 1); dictkeys_decref(oldkeys); } ASSERT_CONSISTENT(mp); } /* Internal version of PyDict_Next that returns a hash value in addition * to the key and value. * Return 1 on success, return 0 when the reached the end of the dictionary * (or if op is not a dictionary) */ int _PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue, Py_hash_t *phash) { Py_ssize_t i; PyDictObject *mp; PyDictKeyEntry *entry_ptr; PyObject *value; if (!PyDict_Check(op)) return 0; mp = (PyDictObject *)op; i = *ppos; if (mp->ma_values) { if (i < 0 || i >= mp->ma_used) return 0; /* values of split table is always dense */ entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; value = mp->ma_values[i]; assert(value != NULL); } else { Py_ssize_t n = mp->ma_keys->dk_nentries; if (i < 0 || i >= n) return 0; entry_ptr = &DK_ENTRIES(mp->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) return 0; value = entry_ptr->me_value; } *ppos = i+1; if (pkey) *pkey = entry_ptr->me_key; if (phash) *phash = entry_ptr->me_hash; if (pvalue) *pvalue = value; return 1; } /* * Iterate over a dict. Use like so: * * Py_ssize_t i; * PyObject *key, *value; * i = 0; # important! i should not otherwise be changed by you * while (PyDict_Next(yourdict, &i, &key, &value)) { * Refer to borrowed references in key and value. * } * * Return 1 on success, return 0 when the reached the end of the dictionary * (or if op is not a dictionary) * * CAUTION: In general, it isn't safe to use PyDict_Next in a loop that * mutates the dict. One exception: it is safe if the loop merely changes * the values associated with the keys (but doesn't insert new keys or * delete keys), via PyDict_SetItem(). */ int PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue) { return _PyDict_Next(op, ppos, pkey, pvalue, NULL); } /* Internal version of dict.pop(). */ PyObject * _PyDict_Pop_KnownHash(PyObject *dict, PyObject *key, Py_hash_t hash, PyObject *deflt) { Py_ssize_t ix, hashpos; PyObject *old_value, *old_key; PyDictKeyEntry *ep; PyDictObject *mp; assert(PyDict_Check(dict)); mp = (PyDictObject *)dict; if (mp->ma_used == 0) { if (deflt) { Py_INCREF(deflt); return deflt; } _PyErr_SetKeyError(key); return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || old_value == NULL) { if (deflt) { Py_INCREF(deflt); return deflt; } _PyErr_SetKeyError(key); return NULL; } // Split table doesn't allow deletion. Combine it. if (_PyDict_HasSplitTable(mp)) { if (dictresize(mp, DK_SIZE(mp->ma_keys))) { return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &old_value); assert(ix >= 0); } hashpos = lookdict_index(mp->ma_keys, hash, ix); assert(hashpos >= 0); assert(old_value != NULL); mp->ma_used--; mp->ma_version_tag = DICT_NEXT_VERSION(); dictkeys_set_index(mp->ma_keys, hashpos, DKIX_DUMMY); ep = &DK_ENTRIES(mp->ma_keys)[ix]; ENSURE_ALLOWS_DELETIONS(mp); old_key = ep->me_key; ep->me_key = NULL; ep->me_value = NULL; Py_DECREF(old_key); ASSERT_CONSISTENT(mp); return old_value; } PyObject * _PyDict_Pop(PyObject *dict, PyObject *key, PyObject *deflt) { Py_hash_t hash; if (((PyDictObject *)dict)->ma_used == 0) { if (deflt) { Py_INCREF(deflt); return deflt; } _PyErr_SetKeyError(key); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } return _PyDict_Pop_KnownHash(dict, key, hash, deflt); } /* Internal version of dict.from_keys(). It is subclass-friendly. */ PyObject * _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value) { PyObject *it; /* iter(iterable) */ PyObject *key; PyObject *d; int status; d = _PyObject_CallNoArg(cls); if (d == NULL) return NULL; if (PyDict_CheckExact(d) && ((PyDictObject *)d)->ma_used == 0) { if (PyDict_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; PyObject *oldvalue; Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; if (dictresize(mp, ESTIMATE_SIZE(PyDict_GET_SIZE(iterable)))) { Py_DECREF(d); return NULL; } while (_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) { if (insertdict(mp, key, hash, value)) { Py_DECREF(d); return NULL; } } return d; } if (PyAnySet_CheckExact(iterable)) { PyDictObject *mp = (PyDictObject *)d; Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; if (dictresize(mp, ESTIMATE_SIZE(PySet_GET_SIZE(iterable)))) { Py_DECREF(d); return NULL; } while (_PySet_NextEntry(iterable, &pos, &key, &hash)) { if (insertdict(mp, key, hash, value)) { Py_DECREF(d); return NULL; } } return d; } } it = PyObject_GetIter(iterable); if (it == NULL){ Py_DECREF(d); return NULL; } if (PyDict_CheckExact(d)) { while ((key = PyIter_Next(it)) != NULL) { status = PyDict_SetItem(d, key, value); Py_DECREF(key); if (status < 0) goto Fail; } } else { while ((key = PyIter_Next(it)) != NULL) { status = PyObject_SetItem(d, key, value); Py_DECREF(key); if (status < 0) goto Fail; } } if (PyErr_Occurred()) goto Fail; Py_DECREF(it); return d; Fail: Py_DECREF(it); Py_DECREF(d); return NULL; } /* Methods */ static void dict_dealloc(PyDictObject *mp) { PyObject **values = mp->ma_values; PyDictKeysObject *keys = mp->ma_keys; Py_ssize_t i, n; /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(mp); Py_TRASHCAN_BEGIN(mp, dict_dealloc) if (values != NULL) { if (values != empty_values) { for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) { Py_XDECREF(values[i]); } free_values(values); } dictkeys_decref(keys); } else if (keys != NULL) { assert(keys->dk_refcnt == 1); dictkeys_decref(keys); } #if PyDict_MAXFREELIST > 0 if (numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type)) { free_list[numfree++] = mp; } else #endif { Py_TYPE(mp)->tp_free((PyObject *)mp); } Py_TRASHCAN_END } static PyObject * dict_repr(PyDictObject *mp) { Py_ssize_t i; PyObject *key = NULL, *value = NULL; _PyUnicodeWriter writer; int first; i = Py_ReprEnter((PyObject *)mp); if (i != 0) { return i > 0 ? PyUnicode_FromString("{...}") : NULL; } if (mp->ma_used == 0) { Py_ReprLeave((PyObject *)mp); return PyUnicode_FromString("{}"); } _PyUnicodeWriter_Init(&writer); writer.overallocate = 1; /* "{" + "1: 2" + ", 3: 4" * (len - 1) + "}" */ writer.min_length = 1 + 4 + (2 + 4) * (mp->ma_used - 1) + 1; if (_PyUnicodeWriter_WriteChar(&writer, '{') < 0) goto error; /* Do repr() on each key+value pair, and insert ": " between them. Note that repr may mutate the dict. */ i = 0; first = 1; while (PyDict_Next((PyObject *)mp, &i, &key, &value)) { PyObject *s; int res; /* Prevent repr from deleting key or value during key format. */ Py_INCREF(key); Py_INCREF(value); if (!first) { if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0) goto error; } first = 0; s = PyObject_Repr(key); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; if (_PyUnicodeWriter_WriteASCIIString(&writer, ": ", 2) < 0) goto error; s = PyObject_Repr(value); if (s == NULL) goto error; res = _PyUnicodeWriter_WriteStr(&writer, s); Py_DECREF(s); if (res < 0) goto error; Py_CLEAR(key); Py_CLEAR(value); } writer.overallocate = 0; if (_PyUnicodeWriter_WriteChar(&writer, '}') < 0) goto error; Py_ReprLeave((PyObject *)mp); return _PyUnicodeWriter_Finish(&writer); error: Py_ReprLeave((PyObject *)mp); _PyUnicodeWriter_Dealloc(&writer); Py_XDECREF(key); Py_XDECREF(value); return NULL; } static Py_ssize_t dict_length(PyDictObject *mp) { return mp->ma_used; } static PyObject * dict_subscript(PyDictObject *mp, PyObject *key) { Py_ssize_t ix; Py_hash_t hash; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || value == NULL) { if (!PyDict_CheckExact(mp)) { /* Look up __missing__ method if we're a subclass. */ PyObject *missing, *res; _Py_IDENTIFIER(__missing__); missing = _PyObject_LookupSpecial((PyObject *)mp, &PyId___missing__); if (missing != NULL) { res = PyObject_CallOneArg(missing, key); Py_DECREF(missing); return res; } else if (PyErr_Occurred()) return NULL; } _PyErr_SetKeyError(key); return NULL; } Py_INCREF(value); return value; } static int dict_ass_sub(PyDictObject *mp, PyObject *v, PyObject *w) { if (w == NULL) return PyDict_DelItem((PyObject *)mp, v); else return PyDict_SetItem((PyObject *)mp, v, w); } static PyMappingMethods dict_as_mapping = { (lenfunc)dict_length, /*mp_length*/ (binaryfunc)dict_subscript, /*mp_subscript*/ (objobjargproc)dict_ass_sub, /*mp_ass_subscript*/ }; static PyObject * dict_keys(PyDictObject *mp) { PyObject *v; Py_ssize_t i, j; PyDictKeyEntry *ep; Py_ssize_t n, offset; PyObject **value_ptr; again: n = mp->ma_used; v = PyList_New(n); if (v == NULL) return NULL; if (n != mp->ma_used) { /* Durnit. The allocations caused the dict to resize. * Just start over, this shouldn't normally happen. */ Py_DECREF(v); goto again; } ep = DK_ENTRIES(mp->ma_keys); if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *); } else { value_ptr = &ep[0].me_value; offset = sizeof(PyDictKeyEntry); } for (i = 0, j = 0; j < n; i++) { if (*value_ptr != NULL) { PyObject *key = ep[i].me_key; Py_INCREF(key); PyList_SET_ITEM(v, j, key); j++; } value_ptr = (PyObject **)(((char *)value_ptr) + offset); } assert(j == n); return v; } static PyObject * dict_values(PyDictObject *mp) { PyObject *v; Py_ssize_t i, j; PyDictKeyEntry *ep; Py_ssize_t n, offset; PyObject **value_ptr; again: n = mp->ma_used; v = PyList_New(n); if (v == NULL) return NULL; if (n != mp->ma_used) { /* Durnit. The allocations caused the dict to resize. * Just start over, this shouldn't normally happen. */ Py_DECREF(v); goto again; } ep = DK_ENTRIES(mp->ma_keys); if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *); } else { value_ptr = &ep[0].me_value; offset = sizeof(PyDictKeyEntry); } for (i = 0, j = 0; j < n; i++) { PyObject *value = *value_ptr; value_ptr = (PyObject **)(((char *)value_ptr) + offset); if (value != NULL) { Py_INCREF(value); PyList_SET_ITEM(v, j, value); j++; } } assert(j == n); return v; } static PyObject * dict_items(PyDictObject *mp) { PyObject *v; Py_ssize_t i, j, n; Py_ssize_t offset; PyObject *item, *key; PyDictKeyEntry *ep; PyObject **value_ptr; /* Preallocate the list of tuples, to avoid allocations during * the loop over the items, which could trigger GC, which * could resize the dict. :-( */ again: n = mp->ma_used; v = PyList_New(n); if (v == NULL) return NULL; for (i = 0; i < n; i++) { item = PyTuple_New(2); if (item == NULL) { Py_DECREF(v); return NULL; } PyList_SET_ITEM(v, i, item); } if (n != mp->ma_used) { /* Durnit. The allocations caused the dict to resize. * Just start over, this shouldn't normally happen. */ Py_DECREF(v); goto again; } /* Nothing we do below makes any function calls. */ ep = DK_ENTRIES(mp->ma_keys); if (mp->ma_values) { value_ptr = mp->ma_values; offset = sizeof(PyObject *); } else { value_ptr = &ep[0].me_value; offset = sizeof(PyDictKeyEntry); } for (i = 0, j = 0; j < n; i++) { PyObject *value = *value_ptr; value_ptr = (PyObject **)(((char *)value_ptr) + offset); if (value != NULL) { key = ep[i].me_key; item = PyList_GET_ITEM(v, j); Py_INCREF(key); PyTuple_SET_ITEM(item, 0, key); Py_INCREF(value); PyTuple_SET_ITEM(item, 1, value); j++; } } assert(j == n); return v; } /*[clinic input] @classmethod dict.fromkeys iterable: object value: object=None / Create a new dictionary with keys from iterable and values set to value. [clinic start generated code]*/ static PyObject * dict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value) /*[clinic end generated code: output=8fb98e4b10384999 input=382ba4855d0f74c3]*/ { return _PyDict_FromKeys((PyObject *)type, iterable, value); } /* Single-arg dict update; used by dict_update_common and operators. */ static int dict_update_arg(PyObject *self, PyObject *arg) { if (PyDict_CheckExact(arg)) { return PyDict_Merge(self, arg, 1); } _Py_IDENTIFIER(keys); PyObject *func; if (_PyObject_LookupAttrId(arg, &PyId_keys, &func) < 0) { return -1; } if (func != NULL) { Py_DECREF(func); return PyDict_Merge(self, arg, 1); } return PyDict_MergeFromSeq2(self, arg, 1); } static int dict_update_common(PyObject *self, PyObject *args, PyObject *kwds, const char *methname) { PyObject *arg = NULL; int result = 0; if (!PyArg_UnpackTuple(args, methname, 0, 1, &arg)) { result = -1; } else if (arg != NULL) { result = dict_update_arg(self, arg); } if (result == 0 && kwds != NULL) { if (PyArg_ValidateKeywordArguments(kwds)) result = PyDict_Merge(self, kwds, 1); else result = -1; } return result; } /* Note: dict.update() uses the METH_VARARGS|METH_KEYWORDS calling convention. Using METH_FASTCALL|METH_KEYWORDS would make dict.update(**dict2) calls slower, see the issue #29312. */ static PyObject * dict_update(PyObject *self, PyObject *args, PyObject *kwds) { if (dict_update_common(self, args, kwds, "update") != -1) Py_RETURN_NONE; return NULL; } /* Update unconditionally replaces existing items. Merge has a 3rd argument 'override'; if set, it acts like Update, otherwise it leaves existing items unchanged. PyDict_{Update,Merge} update/merge from a mapping object. PyDict_MergeFromSeq2 updates/merges from any iterable object producing iterable objects of length 2. */ int PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override) { PyObject *it; /* iter(seq2) */ Py_ssize_t i; /* index into seq2 of current element */ PyObject *item; /* seq2[i] */ PyObject *fast; /* item as a 2-tuple or 2-list */ assert(d != NULL); assert(PyDict_Check(d)); assert(seq2 != NULL); it = PyObject_GetIter(seq2); if (it == NULL) return -1; for (i = 0; ; ++i) { PyObject *key, *value; Py_ssize_t n; fast = NULL; item = PyIter_Next(it); if (item == NULL) { if (PyErr_Occurred()) goto Fail; break; } /* Convert item to sequence, and verify length 2. */ fast = PySequence_Fast(item, ""); if (fast == NULL) { if (PyErr_ExceptionMatches(PyExc_TypeError)) PyErr_Format(PyExc_TypeError, "cannot convert dictionary update " "sequence element #%zd to a sequence", i); goto Fail; } n = PySequence_Fast_GET_SIZE(fast); if (n != 2) { PyErr_Format(PyExc_ValueError, "dictionary update sequence element #%zd " "has length %zd; 2 is required", i, n); goto Fail; } /* Update/merge with this (key, value) pair. */ key = PySequence_Fast_GET_ITEM(fast, 0); value = PySequence_Fast_GET_ITEM(fast, 1); Py_INCREF(key); Py_INCREF(value); if (override) { if (PyDict_SetItem(d, key, value) < 0) { Py_DECREF(key); Py_DECREF(value); goto Fail; } } else if (PyDict_GetItemWithError(d, key) == NULL) { if (PyErr_Occurred() || PyDict_SetItem(d, key, value) < 0) { Py_DECREF(key); Py_DECREF(value); goto Fail; } } Py_DECREF(key); Py_DECREF(value); Py_DECREF(fast); Py_DECREF(item); } i = 0; ASSERT_CONSISTENT(d); goto Return; Fail: Py_XDECREF(item); Py_XDECREF(fast); i = -1; Return: Py_DECREF(it); return Py_SAFE_DOWNCAST(i, Py_ssize_t, int); } static int dict_merge(PyObject *a, PyObject *b, int override) { PyDictObject *mp, *other; Py_ssize_t i, n; PyDictKeyEntry *entry, *ep0; assert(0 <= override && override <= 2); /* We accept for the argument either a concrete dictionary object, * or an abstract "mapping" object. For the former, we can do * things quite efficiently. For the latter, we only require that * PyMapping_Keys() and PyObject_GetItem() be supported. */ if (a == NULL || !PyDict_Check(a) || b == NULL) { PyErr_BadInternalCall(); return -1; } mp = (PyDictObject*)a; if (PyDict_Check(b) && (Py_TYPE(b)->tp_iter == (getiterfunc)dict_iter)) { other = (PyDictObject*)b; if (other == mp || other->ma_used == 0) /* a.update(a) or a.update({}); nothing to do */ return 0; if (mp->ma_used == 0) /* Since the target dict is empty, PyDict_GetItem() * always returns NULL. Setting override to 1 * skips the unnecessary test. */ override = 1; /* Do one big resize at the start, rather than * incrementally resizing as we insert new items. Expect * that there will be no (or few) overlapping keys. */ if (USABLE_FRACTION(mp->ma_keys->dk_size) < other->ma_used) { if (dictresize(mp, ESTIMATE_SIZE(mp->ma_used + other->ma_used))) { return -1; } } ep0 = DK_ENTRIES(other->ma_keys); for (i = 0, n = other->ma_keys->dk_nentries; i < n; i++) { PyObject *key, *value; Py_hash_t hash; entry = &ep0[i]; key = entry->me_key; hash = entry->me_hash; if (other->ma_values) value = other->ma_values[i]; else value = entry->me_value; if (value != NULL) { int err = 0; Py_INCREF(key); Py_INCREF(value); if (override == 1) err = insertdict(mp, key, hash, value); else if (_PyDict_GetItem_KnownHash(a, key, hash) == NULL) { if (PyErr_Occurred()) { Py_DECREF(value); Py_DECREF(key); return -1; } err = insertdict(mp, key, hash, value); } else if (override != 0) { _PyErr_SetKeyError(key); Py_DECREF(value); Py_DECREF(key); return -1; } Py_DECREF(value); Py_DECREF(key); if (err != 0) return -1; if (n != other->ma_keys->dk_nentries) { PyErr_SetString(PyExc_RuntimeError, "dict mutated during update"); return -1; } } } } else { /* Do it the generic, slower way */ PyObject *keys = PyMapping_Keys(b); PyObject *iter; PyObject *key, *value; int status; if (keys == NULL) /* Docstring says this is equivalent to E.keys() so * if E doesn't have a .keys() method we want * AttributeError to percolate up. Might as well * do the same for any other error. */ return -1; iter = PyObject_GetIter(keys); Py_DECREF(keys); if (iter == NULL) return -1; for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) { if (override != 1) { if (PyDict_GetItemWithError(a, key) != NULL) { if (override != 0) { _PyErr_SetKeyError(key); Py_DECREF(key); Py_DECREF(iter); return -1; } Py_DECREF(key); continue; } else if (PyErr_Occurred()) { Py_DECREF(key); Py_DECREF(iter); return -1; } } value = PyObject_GetItem(b, key); if (value == NULL) { Py_DECREF(iter); Py_DECREF(key); return -1; } status = PyDict_SetItem(a, key, value); Py_DECREF(key); Py_DECREF(value); if (status < 0) { Py_DECREF(iter); return -1; } } Py_DECREF(iter); if (PyErr_Occurred()) /* Iterator completed, via error */ return -1; } ASSERT_CONSISTENT(a); return 0; } int PyDict_Update(PyObject *a, PyObject *b) { return dict_merge(a, b, 1); } int PyDict_Merge(PyObject *a, PyObject *b, int override) { /* XXX Deprecate override not in (0, 1). */ return dict_merge(a, b, override != 0); } int _PyDict_MergeEx(PyObject *a, PyObject *b, int override) { return dict_merge(a, b, override); } static PyObject * dict_copy(PyDictObject *mp, PyObject *Py_UNUSED(ignored)) { return PyDict_Copy((PyObject*)mp); } PyObject * PyDict_Copy(PyObject *o) { PyObject *copy; PyDictObject *mp; Py_ssize_t i, n; if (o == NULL || !PyDict_Check(o)) { PyErr_BadInternalCall(); return NULL; } mp = (PyDictObject *)o; if (mp->ma_used == 0) { /* The dict is empty; just return a new dict. */ return PyDict_New(); } if (_PyDict_HasSplitTable(mp)) { PyDictObject *split_copy; Py_ssize_t size = USABLE_FRACTION(DK_SIZE(mp->ma_keys)); PyObject **newvalues; newvalues = new_values(size); if (newvalues == NULL) return PyErr_NoMemory(); split_copy = PyObject_GC_New(PyDictObject, &PyDict_Type); if (split_copy == NULL) { free_values(newvalues); return NULL; } split_copy->ma_values = newvalues; split_copy->ma_keys = mp->ma_keys; split_copy->ma_used = mp->ma_used; split_copy->ma_version_tag = DICT_NEXT_VERSION(); dictkeys_incref(mp->ma_keys); for (i = 0, n = size; i < n; i++) { PyObject *value = mp->ma_values[i]; Py_XINCREF(value); split_copy->ma_values[i] = value; } if (_PyObject_GC_IS_TRACKED(mp)) _PyObject_GC_TRACK(split_copy); return (PyObject *)split_copy; } if (PyDict_CheckExact(mp) && mp->ma_values == NULL && (mp->ma_used >= (mp->ma_keys->dk_nentries * 2) / 3)) { /* Use fast-copy if: (1) 'mp' is an instance of a subclassed dict; and (2) 'mp' is not a split-dict; and (3) if 'mp' is non-compact ('del' operation does not resize dicts), do fast-copy only if it has at most 1/3 non-used keys. The last condition (3) is important to guard against a pathological case when a large dict is almost emptied with multiple del/pop operations and copied after that. In cases like this, we defer to PyDict_Merge, which produces a compacted copy. */ return clone_combined_dict(mp); } copy = PyDict_New(); if (copy == NULL) return NULL; if (PyDict_Merge(copy, o, 1) == 0) return copy; Py_DECREF(copy); return NULL; } Py_ssize_t PyDict_Size(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return -1; } return ((PyDictObject *)mp)->ma_used; } PyObject * PyDict_Keys(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return NULL; } return dict_keys((PyDictObject *)mp); } PyObject * PyDict_Values(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return NULL; } return dict_values((PyDictObject *)mp); } PyObject * PyDict_Items(PyObject *mp) { if (mp == NULL || !PyDict_Check(mp)) { PyErr_BadInternalCall(); return NULL; } return dict_items((PyDictObject *)mp); } /* Return 1 if dicts equal, 0 if not, -1 if error. * Gets out as soon as any difference is detected. * Uses only Py_EQ comparison. */ static int dict_equal(PyDictObject *a, PyDictObject *b) { Py_ssize_t i; if (a->ma_used != b->ma_used) /* can't be equal if # of entries differ */ return 0; /* Same # of entries -- check all of 'em. Exit early on any diff. */ for (i = 0; i < a->ma_keys->dk_nentries; i++) { PyDictKeyEntry *ep = &DK_ENTRIES(a->ma_keys)[i]; PyObject *aval; if (a->ma_values) aval = a->ma_values[i]; else aval = ep->me_value; if (aval != NULL) { int cmp; PyObject *bval; PyObject *key = ep->me_key; /* temporarily bump aval's refcount to ensure it stays alive until we're done with it */ Py_INCREF(aval); /* ditto for key */ Py_INCREF(key); /* reuse the known hash value */ b->ma_keys->dk_lookup(b, key, ep->me_hash, &bval); if (bval == NULL) { Py_DECREF(key); Py_DECREF(aval); if (PyErr_Occurred()) return -1; return 0; } Py_INCREF(bval); cmp = PyObject_RichCompareBool(aval, bval, Py_EQ); Py_DECREF(key); Py_DECREF(aval); Py_DECREF(bval); if (cmp <= 0) /* error or not equal */ return cmp; } } return 1; } static PyObject * dict_richcompare(PyObject *v, PyObject *w, int op) { int cmp; PyObject *res; if (!PyDict_Check(v) || !PyDict_Check(w)) { res = Py_NotImplemented; } else if (op == Py_EQ || op == Py_NE) { cmp = dict_equal((PyDictObject *)v, (PyDictObject *)w); if (cmp < 0) return NULL; res = (cmp == (op == Py_EQ)) ? Py_True : Py_False; } else res = Py_NotImplemented; Py_INCREF(res); return res; } /*[clinic input] @coexist dict.__contains__ key: object / True if the dictionary has the specified key, else False. [clinic start generated code]*/ static PyObject * dict___contains__(PyDictObject *self, PyObject *key) /*[clinic end generated code: output=a3d03db709ed6e6b input=fe1cb42ad831e820]*/ { register PyDictObject *mp = self; Py_hash_t hash; Py_ssize_t ix; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || value == NULL) Py_RETURN_FALSE; Py_RETURN_TRUE; } /*[clinic input] dict.get key: object default: object = None / Return the value for key if key is in the dictionary, else default. [clinic start generated code]*/ static PyObject * dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value) /*[clinic end generated code: output=bba707729dee05bf input=279ddb5790b6b107]*/ { PyObject *val = NULL; Py_hash_t hash; Py_ssize_t ix; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } ix = (self->ma_keys->dk_lookup) (self, key, hash, &val); if (ix == DKIX_ERROR) return NULL; if (ix == DKIX_EMPTY || val == NULL) { val = default_value; } Py_INCREF(val); return val; } PyObject * PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj) { PyDictObject *mp = (PyDictObject *)d; PyObject *value; Py_hash_t hash; if (!PyDict_Check(d)) { PyErr_BadInternalCall(); return NULL; } if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return NULL; } if (mp->ma_keys == Py_EMPTY_KEYS) { if (insert_to_emptydict(mp, key, hash, defaultobj) < 0) { return NULL; } return defaultobj; } if (mp->ma_values != NULL && !PyUnicode_CheckExact(key)) { if (insertion_resize(mp) < 0) return NULL; } Py_ssize_t ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return NULL; if (_PyDict_HasSplitTable(mp) && ((ix >= 0 && value == NULL && mp->ma_used != ix) || (ix == DKIX_EMPTY && mp->ma_used != mp->ma_keys->dk_nentries))) { if (insertion_resize(mp) < 0) { return NULL; } ix = DKIX_EMPTY; } if (ix == DKIX_EMPTY) { PyDictKeyEntry *ep, *ep0; value = defaultobj; if (mp->ma_keys->dk_usable <= 0) { if (insertion_resize(mp) < 0) { return NULL; } } Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash); ep0 = DK_ENTRIES(mp->ma_keys); ep = &ep0[mp->ma_keys->dk_nentries]; dictkeys_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries); Py_INCREF(key); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); ep->me_key = key; ep->me_hash = hash; if (_PyDict_HasSplitTable(mp)) { assert(mp->ma_values[mp->ma_keys->dk_nentries] == NULL); mp->ma_values[mp->ma_keys->dk_nentries] = value; } else { ep->me_value = value; } mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); mp->ma_keys->dk_usable--; mp->ma_keys->dk_nentries++; assert(mp->ma_keys->dk_usable >= 0); } else if (value == NULL) { value = defaultobj; assert(_PyDict_HasSplitTable(mp)); assert(ix == mp->ma_used); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); mp->ma_values[ix] = value; mp->ma_used++; mp->ma_version_tag = DICT_NEXT_VERSION(); } ASSERT_CONSISTENT(mp); return value; } /*[clinic input] dict.setdefault key: object default: object = None / Insert key with a value of default if key is not in the dictionary. Return the value for key if key is in the dictionary, else default. [clinic start generated code]*/ static PyObject * dict_setdefault_impl(PyDictObject *self, PyObject *key, PyObject *default_value) /*[clinic end generated code: output=f8c1101ebf69e220 input=0f063756e815fd9d]*/ { PyObject *val; val = PyDict_SetDefault((PyObject *)self, key, default_value); Py_XINCREF(val); return val; } static PyObject * dict_clear(PyDictObject *mp, PyObject *Py_UNUSED(ignored)) { PyDict_Clear((PyObject *)mp); Py_RETURN_NONE; } /*[clinic input] dict.pop key: object default: object = NULL / D.pop(k[,d]) -> v, remove specified key and return the corresponding value. If key is not found, default is returned if given, otherwise KeyError is raised [clinic start generated code]*/ static PyObject * dict_pop_impl(PyDictObject *self, PyObject *key, PyObject *default_value) /*[clinic end generated code: output=3abb47b89f24c21c input=eeebec7812190348]*/ { return _PyDict_Pop((PyObject*)self, key, default_value); } /*[clinic input] dict.popitem Remove and return a (key, value) pair as a 2-tuple. Pairs are returned in LIFO (last-in, first-out) order. Raises KeyError if the dict is empty. [clinic start generated code]*/ static PyObject * dict_popitem_impl(PyDictObject *self) /*[clinic end generated code: output=e65fcb04420d230d input=1c38a49f21f64941]*/ { Py_ssize_t i, j; PyDictKeyEntry *ep0, *ep; PyObject *res; /* Allocate the result tuple before checking the size. Believe it * or not, this allocation could trigger a garbage collection which * could empty the dict, so if we checked the size first and that * happened, the result would be an infinite loop (searching for an * entry that no longer exists). Note that the usual popitem() * idiom is "while d: k, v = d.popitem()". so needing to throw the * tuple away if the dict *is* empty isn't a significant * inefficiency -- possible, but unlikely in practice. */ res = PyTuple_New(2); if (res == NULL) return NULL; if (self->ma_used == 0) { Py_DECREF(res); PyErr_SetString(PyExc_KeyError, "popitem(): dictionary is empty"); return NULL; } /* Convert split table to combined table */ if (self->ma_keys->dk_lookup == lookdict_split) { if (dictresize(self, DK_SIZE(self->ma_keys))) { Py_DECREF(res); return NULL; } } ENSURE_ALLOWS_DELETIONS(self); /* Pop last item */ ep0 = DK_ENTRIES(self->ma_keys); i = self->ma_keys->dk_nentries - 1; while (i >= 0 && ep0[i].me_value == NULL) { i--; } assert(i >= 0); ep = &ep0[i]; j = lookdict_index(self->ma_keys, ep->me_hash, i); assert(j >= 0); assert(dictkeys_get_index(self->ma_keys, j) == i); dictkeys_set_index(self->ma_keys, j, DKIX_DUMMY); PyTuple_SET_ITEM(res, 0, ep->me_key); PyTuple_SET_ITEM(res, 1, ep->me_value); ep->me_key = NULL; ep->me_value = NULL; /* We can't dk_usable++ since there is DKIX_DUMMY in indices */ self->ma_keys->dk_nentries = i; self->ma_used--; self->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(self); return res; } static int dict_traverse(PyObject *op, visitproc visit, void *arg) { PyDictObject *mp = (PyDictObject *)op; PyDictKeysObject *keys = mp->ma_keys; PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n = keys->dk_nentries; if (keys->dk_lookup == lookdict) { for (i = 0; i < n; i++) { if (entries[i].me_value != NULL) { Py_VISIT(entries[i].me_value); Py_VISIT(entries[i].me_key); } } } else { if (mp->ma_values != NULL) { for (i = 0; i < n; i++) { Py_VISIT(mp->ma_values[i]); } } else { for (i = 0; i < n; i++) { Py_VISIT(entries[i].me_value); } } } return 0; } static int dict_tp_clear(PyObject *op) { PyDict_Clear(op); return 0; } static PyObject *dictiter_new(PyDictObject *, PyTypeObject *); Py_ssize_t _PyDict_SizeOf(PyDictObject *mp) { Py_ssize_t size, usable, res; size = DK_SIZE(mp->ma_keys); usable = USABLE_FRACTION(size); res = _PyObject_SIZE(Py_TYPE(mp)); if (mp->ma_values) res += usable * sizeof(PyObject*); /* If the dictionary is split, the keys portion is accounted-for in the type object. */ if (mp->ma_keys->dk_refcnt == 1) res += (sizeof(PyDictKeysObject) + DK_IXSIZE(mp->ma_keys) * size + sizeof(PyDictKeyEntry) * usable); return res; } Py_ssize_t _PyDict_KeysSize(PyDictKeysObject *keys) { return (sizeof(PyDictKeysObject) + DK_IXSIZE(keys) * DK_SIZE(keys) + USABLE_FRACTION(DK_SIZE(keys)) * sizeof(PyDictKeyEntry)); } static PyObject * dict_sizeof(PyDictObject *mp, PyObject *Py_UNUSED(ignored)) { return PyLong_FromSsize_t(_PyDict_SizeOf(mp)); } static PyObject * dict_or(PyObject *self, PyObject *other) { if (!PyDict_Check(self) || !PyDict_Check(other)) { Py_RETURN_NOTIMPLEMENTED; } PyObject *new = PyDict_Copy(self); if (new == NULL) { return NULL; } if (dict_update_arg(new, other)) { Py_DECREF(new); return NULL; } return new; } static PyObject * dict_ior(PyObject *self, PyObject *other) { if (dict_update_arg(self, other)) { return NULL; } Py_INCREF(self); return self; } PyDoc_STRVAR(getitem__doc__, "x.__getitem__(y) <==> x[y]"); PyDoc_STRVAR(sizeof__doc__, "D.__sizeof__() -> size of D in memory, in bytes"); PyDoc_STRVAR(update__doc__, "D.update([E, ]**F) -> None. Update D from dict/iterable E and F.\n\ If E is present and has a .keys() method, then does: for k in E: D[k] = E[k]\n\ If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v\n\ In either case, this is followed by: for k in F: D[k] = F[k]"); PyDoc_STRVAR(clear__doc__, "D.clear() -> None. Remove all items from D."); PyDoc_STRVAR(copy__doc__, "D.copy() -> a shallow copy of D"); /* Forward */ static PyObject *dictkeys_new(PyObject *, PyObject *); static PyObject *dictitems_new(PyObject *, PyObject *); static PyObject *dictvalues_new(PyObject *, PyObject *); PyDoc_STRVAR(keys__doc__, "D.keys() -> a set-like object providing a view on D's keys"); PyDoc_STRVAR(items__doc__, "D.items() -> a set-like object providing a view on D's items"); PyDoc_STRVAR(values__doc__, "D.values() -> an object providing a view on D's values"); static PyMethodDef mapp_methods[] = { DICT___CONTAINS___METHODDEF {"__getitem__", (PyCFunction)(void(*)(void))dict_subscript, METH_O | METH_COEXIST, getitem__doc__}, {"__sizeof__", (PyCFunction)(void(*)(void))dict_sizeof, METH_NOARGS, sizeof__doc__}, DICT_GET_METHODDEF DICT_SETDEFAULT_METHODDEF DICT_POP_METHODDEF DICT_POPITEM_METHODDEF {"keys", dictkeys_new, METH_NOARGS, keys__doc__}, {"items", dictitems_new, METH_NOARGS, items__doc__}, {"values", dictvalues_new, METH_NOARGS, values__doc__}, {"update", (PyCFunction)(void(*)(void))dict_update, METH_VARARGS | METH_KEYWORDS, update__doc__}, DICT_FROMKEYS_METHODDEF {"clear", (PyCFunction)dict_clear, METH_NOARGS, clear__doc__}, {"copy", (PyCFunction)dict_copy, METH_NOARGS, copy__doc__}, DICT___REVERSED___METHODDEF {"__class_getitem__", (PyCFunction)Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, {NULL, NULL} /* sentinel */ }; /* Return 1 if `key` is in dict `op`, 0 if not, and -1 on error. */ int PyDict_Contains(PyObject *op, PyObject *key) { Py_hash_t hash; Py_ssize_t ix; PyDictObject *mp = (PyDictObject *)op; PyObject *value; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return -1; } ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return -1; return (ix != DKIX_EMPTY && value != NULL); } /* Internal version of PyDict_Contains used when the hash value is already known */ int _PyDict_Contains(PyObject *op, PyObject *key, Py_hash_t hash) { PyDictObject *mp = (PyDictObject *)op; PyObject *value; Py_ssize_t ix; ix = (mp->ma_keys->dk_lookup)(mp, key, hash, &value); if (ix == DKIX_ERROR) return -1; return (ix != DKIX_EMPTY && value != NULL); } /* Hack to implement "key in dict" */ static PySequenceMethods dict_as_sequence = { 0, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ PyDict_Contains, /* sq_contains */ 0, /* sq_inplace_concat */ 0, /* sq_inplace_repeat */ }; static PyNumberMethods dict_as_number = { .nb_or = dict_or, .nb_inplace_or = dict_ior, }; static PyObject * dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyObject *self; PyDictObject *d; assert(type != NULL && type->tp_alloc != NULL); self = type->tp_alloc(type, 0); if (self == NULL) return NULL; d = (PyDictObject *)self; /* The object has been implicitly tracked by tp_alloc */ if (type == &PyDict_Type) _PyObject_GC_UNTRACK(d); d->ma_used = 0; d->ma_version_tag = DICT_NEXT_VERSION(); d->ma_keys = new_keys_object(PyDict_MINSIZE); if (d->ma_keys == NULL) { Py_DECREF(self); return NULL; } ASSERT_CONSISTENT(d); return self; } static int dict_init(PyObject *self, PyObject *args, PyObject *kwds) { return dict_update_common(self, args, kwds, "dict"); } static PyObject * dict_vectorcall(PyObject *type, PyObject * const*args, size_t nargsf, PyObject *kwnames) { assert(PyType_Check(type)); Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); if (!_PyArg_CheckPositional("dict", nargs, 0, 1)) { return NULL; } PyObject *self = dict_new((PyTypeObject *)type, NULL, NULL); if (self == NULL) { return NULL; } if (nargs == 1) { if (dict_update_arg(self, args[0]) < 0) { Py_DECREF(self); return NULL; } args++; } if (kwnames != NULL) { for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(kwnames); i++) { if (PyDict_SetItem(self, PyTuple_GET_ITEM(kwnames, i), args[i]) < 0) { Py_DECREF(self); return NULL; } } } return self; } static PyObject * dict_iter(PyDictObject *dict) { return dictiter_new(dict, &PyDictIterKey_Type); } PyDoc_STRVAR(dictionary_doc, "dict() -> new empty dictionary\n" "dict(mapping) -> new dictionary initialized from a mapping object's\n" " (key, value) pairs\n" "dict(iterable) -> new dictionary initialized as if via:\n" " d = {}\n" " for k, v in iterable:\n" " d[k] = v\n" "dict(**kwargs) -> new dictionary initialized with the name=value pairs\n" " in the keyword argument list. For example: dict(one=1, two=2)"); PyTypeObject PyDict_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict", sizeof(PyDictObject), 0, (destructor)dict_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dict_repr, /* tp_repr */ &dict_as_number, /* tp_as_number */ &dict_as_sequence, /* tp_as_sequence */ &dict_as_mapping, /* tp_as_mapping */ PyObject_HashNotImplemented, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_DICT_SUBCLASS, /* tp_flags */ dictionary_doc, /* tp_doc */ dict_traverse, /* tp_traverse */ dict_tp_clear, /* tp_clear */ dict_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dict_iter, /* tp_iter */ 0, /* tp_iternext */ mapp_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ dict_init, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ dict_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ .tp_vectorcall = dict_vectorcall, }; PyObject * _PyDict_GetItemId(PyObject *dp, struct _Py_Identifier *key) { PyObject *kv; kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) { PyErr_Clear(); return NULL; } return PyDict_GetItem(dp, kv); } /* For backward compatibility with old dictionary interface */ PyObject * PyDict_GetItemString(PyObject *v, const char *key) { PyObject *kv, *rv; kv = PyUnicode_FromString(key); if (kv == NULL) { PyErr_Clear(); return NULL; } rv = PyDict_GetItem(v, kv); Py_DECREF(kv); return rv; } int _PyDict_SetItemId(PyObject *v, struct _Py_Identifier *key, PyObject *item) { PyObject *kv; kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) return -1; return PyDict_SetItem(v, kv, item); } int PyDict_SetItemString(PyObject *v, const char *key, PyObject *item) { PyObject *kv; int err; kv = PyUnicode_FromString(key); if (kv == NULL) return -1; PyUnicode_InternInPlace(&kv); /* XXX Should we really? */ err = PyDict_SetItem(v, kv, item); Py_DECREF(kv); return err; } int _PyDict_DelItemId(PyObject *v, _Py_Identifier *key) { PyObject *kv = _PyUnicode_FromId(key); /* borrowed */ if (kv == NULL) return -1; return PyDict_DelItem(v, kv); } int PyDict_DelItemString(PyObject *v, const char *key) { PyObject *kv; int err; kv = PyUnicode_FromString(key); if (kv == NULL) return -1; err = PyDict_DelItem(v, kv); Py_DECREF(kv); return err; } /* Dictionary iterator types */ typedef struct { PyObject_HEAD PyDictObject *di_dict; /* Set to NULL when iterator is exhausted */ Py_ssize_t di_used; Py_ssize_t di_pos; PyObject* di_result; /* reusable result tuple for iteritems */ Py_ssize_t len; } dictiterobject; static PyObject * dictiter_new(PyDictObject *dict, PyTypeObject *itertype) { dictiterobject *di; di = PyObject_GC_New(dictiterobject, itertype); if (di == NULL) { return NULL; } Py_INCREF(dict); di->di_dict = dict; di->di_used = dict->ma_used; di->len = dict->ma_used; if (itertype == &PyDictRevIterKey_Type || itertype == &PyDictRevIterItem_Type || itertype == &PyDictRevIterValue_Type) { if (dict->ma_values) { di->di_pos = dict->ma_used - 1; } else { di->di_pos = dict->ma_keys->dk_nentries - 1; } } else { di->di_pos = 0; } if (itertype == &PyDictIterItem_Type || itertype == &PyDictRevIterItem_Type) { di->di_result = PyTuple_Pack(2, Py_None, Py_None); if (di->di_result == NULL) { Py_DECREF(di); return NULL; } } else { di->di_result = NULL; } _PyObject_GC_TRACK(di); return (PyObject *)di; } static void dictiter_dealloc(dictiterobject *di) { /* bpo-31095: UnTrack is needed before calling any callbacks */ _PyObject_GC_UNTRACK(di); Py_XDECREF(di->di_dict); Py_XDECREF(di->di_result); PyObject_GC_Del(di); } static int dictiter_traverse(dictiterobject *di, visitproc visit, void *arg) { Py_VISIT(di->di_dict); Py_VISIT(di->di_result); return 0; } static PyObject * dictiter_len(dictiterobject *di, PyObject *Py_UNUSED(ignored)) { Py_ssize_t len = 0; if (di->di_dict != NULL && di->di_used == di->di_dict->ma_used) len = di->len; return PyLong_FromSize_t(len); } PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); static PyObject * dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reduce_doc, "Return state information for pickling."); static PyMethodDef dictiter_methods[] = { {"__length_hint__", (PyCFunction)(void(*)(void))dictiter_len, METH_NOARGS, length_hint_doc}, {"__reduce__", (PyCFunction)(void(*)(void))dictiter_reduce, METH_NOARGS, reduce_doc}, {NULL, NULL} /* sentinel */ }; static PyObject* dictiter_iternextkey(dictiterobject *di) { PyObject *key; Py_ssize_t i; PyDictKeysObject *k; PyDictObject *d = di->di_dict; if (d == NULL) return NULL; assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } i = di->di_pos; k = d->ma_keys; assert(i >= 0); if (d->ma_values) { if (i >= d->ma_used) goto fail; key = DK_ENTRIES(k)[i].me_key; assert(d->ma_values[i] != NULL); } else { Py_ssize_t n = k->dk_nentries; PyDictKeyEntry *entry_ptr = &DK_ENTRIES(k)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) goto fail; key = entry_ptr->me_key; } // We found an element (key), but did not expect it if (di->len == 0) { PyErr_SetString(PyExc_RuntimeError, "dictionary keys changed during iteration"); goto fail; } di->di_pos = i+1; di->len--; Py_INCREF(key); return key; fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictIterKey_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_keyiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)dictiter_iternextkey, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject * dictiter_iternextvalue(dictiterobject *di) { PyObject *value; Py_ssize_t i; PyDictObject *d = di->di_dict; if (d == NULL) return NULL; assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } i = di->di_pos; assert(i >= 0); if (d->ma_values) { if (i >= d->ma_used) goto fail; value = d->ma_values[i]; assert(value != NULL); } else { Py_ssize_t n = d->ma_keys->dk_nentries; PyDictKeyEntry *entry_ptr = &DK_ENTRIES(d->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) goto fail; value = entry_ptr->me_value; } // We found an element, but did not expect it if (di->len == 0) { PyErr_SetString(PyExc_RuntimeError, "dictionary keys changed during iteration"); goto fail; } di->di_pos = i+1; di->len--; Py_INCREF(value); return value; fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictIterValue_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_valueiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)dictiter_iternextvalue, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject * dictiter_iternextitem(dictiterobject *di) { PyObject *key, *value, *result; Py_ssize_t i; PyDictObject *d = di->di_dict; if (d == NULL) return NULL; assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } i = di->di_pos; assert(i >= 0); if (d->ma_values) { if (i >= d->ma_used) goto fail; key = DK_ENTRIES(d->ma_keys)[i].me_key; value = d->ma_values[i]; assert(value != NULL); } else { Py_ssize_t n = d->ma_keys->dk_nentries; PyDictKeyEntry *entry_ptr = &DK_ENTRIES(d->ma_keys)[i]; while (i < n && entry_ptr->me_value == NULL) { entry_ptr++; i++; } if (i >= n) goto fail; key = entry_ptr->me_key; value = entry_ptr->me_value; } // We found an element, but did not expect it if (di->len == 0) { PyErr_SetString(PyExc_RuntimeError, "dictionary keys changed during iteration"); goto fail; } di->di_pos = i+1; di->len--; Py_INCREF(key); Py_INCREF(value); result = di->di_result; if (Py_REFCNT(result) == 1) { PyObject *oldkey = PyTuple_GET_ITEM(result, 0); PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); } else { result = PyTuple_New(2); if (result == NULL) return NULL; PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ } return result; fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictIterItem_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_itemiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)dictiter_iternextitem, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; /* dictreviter */ static PyObject * dictreviter_iternext(dictiterobject *di) { PyDictObject *d = di->di_dict; if (d == NULL) { return NULL; } assert (PyDict_Check(d)); if (di->di_used != d->ma_used) { PyErr_SetString(PyExc_RuntimeError, "dictionary changed size during iteration"); di->di_used = -1; /* Make this state sticky */ return NULL; } Py_ssize_t i = di->di_pos; PyDictKeysObject *k = d->ma_keys; PyObject *key, *value, *result; if (i < 0) { goto fail; } if (d->ma_values) { key = DK_ENTRIES(k)[i].me_key; value = d->ma_values[i]; assert (value != NULL); } else { PyDictKeyEntry *entry_ptr = &DK_ENTRIES(k)[i]; while (entry_ptr->me_value == NULL) { if (--i < 0) { goto fail; } entry_ptr--; } key = entry_ptr->me_key; value = entry_ptr->me_value; } di->di_pos = i-1; di->len--; if (Py_IS_TYPE(di, &PyDictRevIterKey_Type)) { Py_INCREF(key); return key; } else if (Py_IS_TYPE(di, &PyDictRevIterValue_Type)) { Py_INCREF(value); return value; } else if (Py_IS_TYPE(di, &PyDictRevIterItem_Type)) { Py_INCREF(key); Py_INCREF(value); result = di->di_result; if (Py_REFCNT(result) == 1) { PyObject *oldkey = PyTuple_GET_ITEM(result, 0); PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); } else { result = PyTuple_New(2); if (result == NULL) { return NULL; } PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, value); /* steals reference */ } return result; } else { Py_UNREACHABLE(); } fail: di->di_dict = NULL; Py_DECREF(d); return NULL; } PyTypeObject PyDictRevIterKey_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_reversekeyiterator", sizeof(dictiterobject), .tp_dealloc = (destructor)dictiter_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_traverse = (traverseproc)dictiter_traverse, .tp_iter = PyObject_SelfIter, .tp_iternext = (iternextfunc)dictreviter_iternext, .tp_methods = dictiter_methods }; /*[clinic input] dict.__reversed__ Return a reverse iterator over the dict keys. [clinic start generated code]*/ static PyObject * dict___reversed___impl(PyDictObject *self) /*[clinic end generated code: output=e674483336d1ed51 input=23210ef3477d8c4d]*/ { assert (PyDict_Check(self)); return dictiter_new(self, &PyDictRevIterKey_Type); } static PyObject * dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored)) { _Py_IDENTIFIER(iter); /* copy the iterator state */ dictiterobject tmp = *di; Py_XINCREF(tmp.di_dict); PyObject *list = PySequence_List((PyObject*)&tmp); Py_XDECREF(tmp.di_dict); if (list == NULL) { return NULL; } return Py_BuildValue("N(N)", _PyEval_GetBuiltinId(&PyId_iter), list); } PyTypeObject PyDictRevIterItem_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_reverseitemiterator", sizeof(dictiterobject), .tp_dealloc = (destructor)dictiter_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_traverse = (traverseproc)dictiter_traverse, .tp_iter = PyObject_SelfIter, .tp_iternext = (iternextfunc)dictreviter_iternext, .tp_methods = dictiter_methods }; PyTypeObject PyDictRevIterValue_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_reversevalueiterator", sizeof(dictiterobject), .tp_dealloc = (destructor)dictiter_dealloc, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_traverse = (traverseproc)dictiter_traverse, .tp_iter = PyObject_SelfIter, .tp_iternext = (iternextfunc)dictreviter_iternext, .tp_methods = dictiter_methods }; /***********************************************/ /* View objects for keys(), items(), values(). */ /***********************************************/ /* The instance lay-out is the same for all three; but the type differs. */ static void dictview_dealloc(_PyDictViewObject *dv) { /* bpo-31095: UnTrack is needed before calling any callbacks */ _PyObject_GC_UNTRACK(dv); Py_XDECREF(dv->dv_dict); PyObject_GC_Del(dv); } static int dictview_traverse(_PyDictViewObject *dv, visitproc visit, void *arg) { Py_VISIT(dv->dv_dict); return 0; } static Py_ssize_t dictview_len(_PyDictViewObject *dv) { Py_ssize_t len = 0; if (dv->dv_dict != NULL) len = dv->dv_dict->ma_used; return len; } PyObject * _PyDictView_New(PyObject *dict, PyTypeObject *type) { _PyDictViewObject *dv; if (dict == NULL) { PyErr_BadInternalCall(); return NULL; } if (!PyDict_Check(dict)) { /* XXX Get rid of this restriction later */ PyErr_Format(PyExc_TypeError, "%s() requires a dict argument, not '%s'", type->tp_name, Py_TYPE(dict)->tp_name); return NULL; } dv = PyObject_GC_New(_PyDictViewObject, type); if (dv == NULL) return NULL; Py_INCREF(dict); dv->dv_dict = (PyDictObject *)dict; _PyObject_GC_TRACK(dv); return (PyObject *)dv; } /* TODO(guido): The views objects are not complete: * support more set operations * support arbitrary mappings? - either these should be static or exported in dictobject.h - if public then they should probably be in builtins */ /* Return 1 if self is a subset of other, iterating over self; 0 if not; -1 if an error occurred. */ static int all_contained_in(PyObject *self, PyObject *other) { PyObject *iter = PyObject_GetIter(self); int ok = 1; if (iter == NULL) return -1; for (;;) { PyObject *next = PyIter_Next(iter); if (next == NULL) { if (PyErr_Occurred()) ok = -1; break; } ok = PySequence_Contains(other, next); Py_DECREF(next); if (ok <= 0) break; } Py_DECREF(iter); return ok; } static PyObject * dictview_richcompare(PyObject *self, PyObject *other, int op) { Py_ssize_t len_self, len_other; int ok; PyObject *result; assert(self != NULL); assert(PyDictViewSet_Check(self)); assert(other != NULL); if (!PyAnySet_Check(other) && !PyDictViewSet_Check(other)) Py_RETURN_NOTIMPLEMENTED; len_self = PyObject_Size(self); if (len_self < 0) return NULL; len_other = PyObject_Size(other); if (len_other < 0) return NULL; ok = 0; switch(op) { case Py_NE: case Py_EQ: if (len_self == len_other) ok = all_contained_in(self, other); if (op == Py_NE && ok >= 0) ok = !ok; break; case Py_LT: if (len_self < len_other) ok = all_contained_in(self, other); break; case Py_LE: if (len_self <= len_other) ok = all_contained_in(self, other); break; case Py_GT: if (len_self > len_other) ok = all_contained_in(other, self); break; case Py_GE: if (len_self >= len_other) ok = all_contained_in(other, self); break; } if (ok < 0) return NULL; result = ok ? Py_True : Py_False; Py_INCREF(result); return result; } static PyObject * dictview_repr(_PyDictViewObject *dv) { PyObject *seq; PyObject *result = NULL; Py_ssize_t rc; rc = Py_ReprEnter((PyObject *)dv); if (rc != 0) { return rc > 0 ? PyUnicode_FromString("...") : NULL; } seq = PySequence_List((PyObject *)dv); if (seq == NULL) { goto Done; } result = PyUnicode_FromFormat("%s(%R)", Py_TYPE(dv)->tp_name, seq); Py_DECREF(seq); Done: Py_ReprLeave((PyObject *)dv); return result; } /*** dict_keys ***/ static PyObject * dictkeys_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictIterKey_Type); } static int dictkeys_contains(_PyDictViewObject *dv, PyObject *obj) { if (dv->dv_dict == NULL) return 0; return PyDict_Contains((PyObject *)dv->dv_dict, obj); } static PySequenceMethods dictkeys_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictkeys_contains, /* sq_contains */ }; // Create an set object from dictviews object. // Returns a new reference. // This utility function is used by set operations. static PyObject* dictviews_to_set(PyObject *self) { PyObject *left = self; if (PyDictKeys_Check(self)) { // PySet_New() has fast path for the dict object. PyObject *dict = (PyObject *)((_PyDictViewObject *)self)->dv_dict; if (PyDict_CheckExact(dict)) { left = dict; } } return PySet_New(left); } static PyObject* dictviews_sub(PyObject *self, PyObject *other) { PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } _Py_IDENTIFIER(difference_update); PyObject *tmp = _PyObject_CallMethodIdOneArg( result, &PyId_difference_update, other); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static int dictitems_contains(_PyDictViewObject *dv, PyObject *obj); PyObject * _PyDictView_Intersect(PyObject* self, PyObject *other) { PyObject *result; PyObject *it; PyObject *key; Py_ssize_t len_self; int rv; int (*dict_contains)(_PyDictViewObject *, PyObject *); /* Python interpreter swaps parameters when dict view is on right side of & */ if (!PyDictViewSet_Check(self)) { PyObject *tmp = other; other = self; self = tmp; } len_self = dictview_len((_PyDictViewObject *)self); /* if other is a set and self is smaller than other, reuse set intersection logic */ if (Py_IS_TYPE(other, &PySet_Type) && len_self <= PyObject_Size(other)) { _Py_IDENTIFIER(intersection); return _PyObject_CallMethodIdObjArgs(other, &PyId_intersection, self, NULL); } /* if other is another dict view, and it is bigger than self, swap them */ if (PyDictViewSet_Check(other)) { Py_ssize_t len_other = dictview_len((_PyDictViewObject *)other); if (len_other > len_self) { PyObject *tmp = other; other = self; self = tmp; } } /* at this point, two things should be true 1. self is a dictview 2. if other is a dictview then it is smaller than self */ result = PySet_New(NULL); if (result == NULL) return NULL; it = PyObject_GetIter(other); if (it == NULL) { Py_DECREF(result); return NULL; } if (PyDictKeys_Check(self)) { dict_contains = dictkeys_contains; } /* else PyDictItems_Check(self) */ else { dict_contains = dictitems_contains; } while ((key = PyIter_Next(it)) != NULL) { rv = dict_contains((_PyDictViewObject *)self, key); if (rv < 0) { goto error; } if (rv) { if (PySet_Add(result, key)) { goto error; } } Py_DECREF(key); } Py_DECREF(it); if (PyErr_Occurred()) { Py_DECREF(result); return NULL; } return result; error: Py_DECREF(it); Py_DECREF(result); Py_DECREF(key); return NULL; } static PyObject* dictviews_or(PyObject* self, PyObject *other) { PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } if (_PySet_Update(result, other) < 0) { Py_DECREF(result); return NULL; } return result; } static PyObject* dictviews_xor(PyObject* self, PyObject *other) { PyObject *result = dictviews_to_set(self); if (result == NULL) { return NULL; } _Py_IDENTIFIER(symmetric_difference_update); PyObject *tmp = _PyObject_CallMethodIdOneArg( result, &PyId_symmetric_difference_update, other); if (tmp == NULL) { Py_DECREF(result); return NULL; } Py_DECREF(tmp); return result; } static PyNumberMethods dictviews_as_number = { 0, /*nb_add*/ (binaryfunc)dictviews_sub, /*nb_subtract*/ 0, /*nb_multiply*/ 0, /*nb_remainder*/ 0, /*nb_divmod*/ 0, /*nb_power*/ 0, /*nb_negative*/ 0, /*nb_positive*/ 0, /*nb_absolute*/ 0, /*nb_bool*/ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ (binaryfunc)_PyDictView_Intersect, /*nb_and*/ (binaryfunc)dictviews_xor, /*nb_xor*/ (binaryfunc)dictviews_or, /*nb_or*/ }; static PyObject* dictviews_isdisjoint(PyObject *self, PyObject *other) { PyObject *it; PyObject *item = NULL; if (self == other) { if (dictview_len((_PyDictViewObject *)self) == 0) Py_RETURN_TRUE; else Py_RETURN_FALSE; } /* Iterate over the shorter object (only if other is a set, * because PySequence_Contains may be expensive otherwise): */ if (PyAnySet_Check(other) || PyDictViewSet_Check(other)) { Py_ssize_t len_self = dictview_len((_PyDictViewObject *)self); Py_ssize_t len_other = PyObject_Size(other); if (len_other == -1) return NULL; if ((len_other > len_self)) { PyObject *tmp = other; other = self; self = tmp; } } it = PyObject_GetIter(other); if (it == NULL) return NULL; while ((item = PyIter_Next(it)) != NULL) { int contains = PySequence_Contains(self, item); Py_DECREF(item); if (contains == -1) { Py_DECREF(it); return NULL; } if (contains) { Py_DECREF(it); Py_RETURN_FALSE; } } Py_DECREF(it); if (PyErr_Occurred()) return NULL; /* PyIter_Next raised an exception. */ Py_RETURN_TRUE; } PyDoc_STRVAR(isdisjoint_doc, "Return True if the view and the given iterable have a null intersection."); static PyObject* dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)); PyDoc_STRVAR(reversed_keys_doc, "Return a reverse iterator over the dict keys."); static PyMethodDef dictkeys_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {"__reversed__", (PyCFunction)(void(*)(void))dictkeys_reversed, METH_NOARGS, reversed_keys_doc}, {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictKeys_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_keys", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictkeys_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dictkeys_iter, /* tp_iter */ 0, /* tp_iternext */ dictkeys_methods, /* tp_methods */ 0, }; static PyObject * dictkeys_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _PyDictView_New(dict, &PyDictKeys_Type); } static PyObject * dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored)) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterKey_Type); } /*** dict_items ***/ static PyObject * dictitems_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictIterItem_Type); } static int dictitems_contains(_PyDictViewObject *dv, PyObject *obj) { int result; PyObject *key, *value, *found; if (dv->dv_dict == NULL) return 0; if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) return 0; key = PyTuple_GET_ITEM(obj, 0); value = PyTuple_GET_ITEM(obj, 1); found = PyDict_GetItemWithError((PyObject *)dv->dv_dict, key); if (found == NULL) { if (PyErr_Occurred()) return -1; return 0; } Py_INCREF(found); result = PyObject_RichCompareBool(found, value, Py_EQ); Py_DECREF(found); return result; } static PySequenceMethods dictitems_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)dictitems_contains, /* sq_contains */ }; static PyObject* dictitems_reversed(_PyDictViewObject *dv); PyDoc_STRVAR(reversed_items_doc, "Return a reverse iterator over the dict items."); static PyMethodDef dictitems_methods[] = { {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O, isdisjoint_doc}, {"__reversed__", (PyCFunction)(void(*)(void))dictitems_reversed, METH_NOARGS, reversed_items_doc}, {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictItems_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_items", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictitems_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dictitems_iter, /* tp_iter */ 0, /* tp_iternext */ dictitems_methods, /* tp_methods */ 0, }; static PyObject * dictitems_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _PyDictView_New(dict, &PyDictItems_Type); } static PyObject * dictitems_reversed(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterItem_Type); } /*** dict_values ***/ static PyObject * dictvalues_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictIterValue_Type); } static PySequenceMethods dictvalues_as_sequence = { (lenfunc)dictview_len, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ 0, /* sq_item */ 0, /* sq_slice */ 0, /* sq_ass_item */ 0, /* sq_ass_slice */ (objobjproc)0, /* sq_contains */ }; static PyObject* dictvalues_reversed(_PyDictViewObject *dv); PyDoc_STRVAR(reversed_values_doc, "Return a reverse iterator over the dict values."); static PyMethodDef dictvalues_methods[] = { {"__reversed__", (PyCFunction)(void(*)(void))dictvalues_reversed, METH_NOARGS, reversed_values_doc}, {NULL, NULL} /* sentinel */ }; PyTypeObject PyDictValues_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "dict_values", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ 0, /* tp_as_number */ &dictvalues_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)dictvalues_iter, /* tp_iter */ 0, /* tp_iternext */ dictvalues_methods, /* tp_methods */ 0, }; static PyObject * dictvalues_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _PyDictView_New(dict, &PyDictValues_Type); } static PyObject * dictvalues_reversed(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyDictRevIterValue_Type); } /* Returns NULL if cannot allocate a new PyDictKeysObject, but does not set an error */ PyDictKeysObject * _PyDict_NewKeysForClass(void) { PyDictKeysObject *keys = new_keys_object(PyDict_MINSIZE); if (keys == NULL) PyErr_Clear(); else keys->dk_lookup = lookdict_split; return keys; } #define CACHED_KEYS(tp) (((PyHeapTypeObject*)tp)->ht_cached_keys) PyObject * PyObject_GenericGetDict(PyObject *obj, void *context) { PyObject *dict, **dictptr = _PyObject_GetDictPtr(obj); if (dictptr == NULL) { PyErr_SetString(PyExc_AttributeError, "This object has no __dict__"); return NULL; } dict = *dictptr; if (dict == NULL) { PyTypeObject *tp = Py_TYPE(obj); if ((tp->tp_flags & Py_TPFLAGS_HEAPTYPE) && CACHED_KEYS(tp)) { dictkeys_incref(CACHED_KEYS(tp)); *dictptr = dict = new_dict_with_shared_keys(CACHED_KEYS(tp)); } else { *dictptr = dict = PyDict_New(); } } Py_XINCREF(dict); return dict; } int _PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr, PyObject *key, PyObject *value) { PyObject *dict; int res; PyDictKeysObject *cached; assert(dictptr != NULL); if ((tp->tp_flags & Py_TPFLAGS_HEAPTYPE) && (cached = CACHED_KEYS(tp))) { assert(dictptr != NULL); dict = *dictptr; if (dict == NULL) { dictkeys_incref(cached); dict = new_dict_with_shared_keys(cached); if (dict == NULL) return -1; *dictptr = dict; } if (value == NULL) { res = PyDict_DelItem(dict, key); // Since key sharing dict doesn't allow deletion, PyDict_DelItem() // always converts dict to combined form. if ((cached = CACHED_KEYS(tp)) != NULL) { CACHED_KEYS(tp) = NULL; dictkeys_decref(cached); } } else { int was_shared = (cached == ((PyDictObject *)dict)->ma_keys); res = PyDict_SetItem(dict, key, value); if (was_shared && (cached = CACHED_KEYS(tp)) != NULL && cached != ((PyDictObject *)dict)->ma_keys) { /* PyDict_SetItem() may call dictresize and convert split table * into combined table. In such case, convert it to split * table again and update type's shared key only when this is * the only dict sharing key with the type. * * This is to allow using shared key in class like this: * * class C: * def __init__(self): * # one dict resize happens * self.a, self.b, self.c = 1, 2, 3 * self.d, self.e, self.f = 4, 5, 6 * a = C() */ if (cached->dk_refcnt == 1) { CACHED_KEYS(tp) = make_keys_shared(dict); } else { CACHED_KEYS(tp) = NULL; } dictkeys_decref(cached); if (CACHED_KEYS(tp) == NULL && PyErr_Occurred()) return -1; } } } else { dict = *dictptr; if (dict == NULL) { dict = PyDict_New(); if (dict == NULL) return -1; *dictptr = dict; } if (value == NULL) { res = PyDict_DelItem(dict, key); } else { res = PyDict_SetItem(dict, key, value); } } return res; } void _PyDictKeys_DecRef(PyDictKeysObject *keys) { dictkeys_decref(keys); } python-frozendict/src/frozendict/c_src/3_9/cpython_src/Objects/stringlib/000077500000000000000000000000001470250572200271125ustar00rootroot00000000000000python-frozendict/src/frozendict/c_src/3_9/cpython_src/Objects/stringlib/eq.h000066400000000000000000000015201470250572200276660ustar00rootroot00000000000000/* Fast unicode equal function optimized for dictobject.c and setobject.c */ /* Return 1 if two unicode objects are equal, 0 if not. * unicode_eq() is called when the hash of two unicode objects is equal. */ Py_LOCAL_INLINE(int) unicode_eq(PyObject *aa, PyObject *bb) { assert(PyUnicode_Check(aa)); assert(PyUnicode_Check(bb)); assert(PyUnicode_IS_READY(aa)); assert(PyUnicode_IS_READY(bb)); PyUnicodeObject *a = (PyUnicodeObject *)aa; PyUnicodeObject *b = (PyUnicodeObject *)bb; if (PyUnicode_GET_LENGTH(a) != PyUnicode_GET_LENGTH(b)) return 0; if (PyUnicode_GET_LENGTH(a) == 0) return 1; if (PyUnicode_KIND(a) != PyUnicode_KIND(b)) return 0; return memcmp(PyUnicode_1BYTE_DATA(a), PyUnicode_1BYTE_DATA(b), PyUnicode_GET_LENGTH(a) * PyUnicode_KIND(a)) == 0; } python-frozendict/src/frozendict/c_src/3_9/cpython_src/other.c000066400000000000000000000013351470250572200250130ustar00rootroot00000000000000static const unsigned int BitLengthTable[32] = { 0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }; unsigned int _Py_bit_length(unsigned long d) { unsigned int d_bits = 0; while (d >= 32) { d_bits += 6; d >>= 6; } d_bits += BitLengthTable[d]; return d_bits; } typedef struct { uintptr_t _gc_next; uintptr_t _gc_prev; } PyGC_Head; #define _Py_AS_GC(o) ((PyGC_Head *)(o)-1) #define _PyObject_GC_IS_TRACKED(o) (_Py_AS_GC(o)->_gc_next != 0) #define _PyObject_GC_MAY_BE_TRACKED(obj) \ (PyObject_IS_GC(obj) && \ (!PyTuple_CheckExact(obj) || _PyObject_GC_IS_TRACKED(obj))) #define PySet_CheckExact(op) Py_IS_TYPE(op, &PySet_Type) python-frozendict/src/frozendict/c_src/3_9/frozendictobject.c000066400000000000000000001661111470250572200247010ustar00rootroot00000000000000#include #include "frozendictobject.h" static PyObject* frozendict_iter(PyDictObject *dict); static int frozendict_equal(PyDictObject* a, PyDictObject* b); #include "other.c" #include "dictobject.c" static void frozendict_free_keys_object(PyDictKeysObject *keys, const int decref_items) { if (decref_items) { PyDictKeyEntry *entries = DK_ENTRIES(keys); Py_ssize_t i, n; for (i = 0, n = keys->dk_nentries; i < n; i++) { Py_XDECREF(entries[i].me_key); Py_XDECREF(entries[i].me_value); } } #if PyDict_MAXFREELIST > 0 if (keys->dk_size == PyDict_MINSIZE && numfreekeys < PyDict_MAXFREELIST) { keys_free_list[numfreekeys++] = keys; return; } #endif PyObject_Free(keys); } static inline void frozendict_keys_decref(PyDictKeysObject *dk, const int decref_items) { assert(dk->dk_refcnt > 0); #ifdef Py_REF_DEBUG _Py_RefTotal--; #endif if (--dk->dk_refcnt == 0) { frozendict_free_keys_object(dk, decref_items); } } static int frozendict_resize(PyDictObject* mp, Py_ssize_t minsize) { const Py_ssize_t newsize = calculate_keysize(minsize); if (newsize <= 0) { PyErr_NoMemory(); return -1; } assert(IS_POWER_OF_2(newsize)); assert(newsize >= PyDict_MINSIZE); PyDictKeysObject* oldkeys = mp->ma_keys; /* Allocate a new table. */ PyDictKeysObject* new_keys = new_keys_object(newsize); if (new_keys == NULL) { return -1; } // New table must be large enough. assert(new_keys->dk_usable >= mp->ma_used); new_keys->dk_lookup = oldkeys->dk_lookup; const Py_ssize_t numentries = mp->ma_used; PyDictKeyEntry* newentries = DK_ENTRIES(new_keys); memcpy( newentries, DK_ENTRIES(oldkeys), numentries * sizeof(PyDictKeyEntry) ); build_indices(new_keys, newentries, numentries); new_keys->dk_usable -= numentries; new_keys->dk_nentries = numentries; // do not decref the keys inside! frozendict_keys_decref(oldkeys, 0); mp->ma_keys = new_keys; return 0; } static int frozendict_insert(PyDictObject *mp, PyObject *key, const Py_hash_t hash, PyObject *value, int empty) { Py_ssize_t ix; PyObject *old_value; PyDictKeysObject* keys = mp->ma_keys; Py_INCREF(key); Py_INCREF(value); MAINTAIN_TRACKING(mp, key, value); if (! empty) { ix = keys->dk_lookup(mp, key, hash, &old_value); if (ix == DKIX_ERROR) { Py_DECREF(value); Py_DECREF(key); return -1; } empty = (ix == DKIX_EMPTY); } if (empty) { /* Insert into new slot. */ if (mp->ma_keys->dk_usable <= 0) { /* Need to resize. */ if (frozendict_resize(mp, GROWTH_RATE(mp))) { Py_DECREF(value); Py_DECREF(key); return -1; } // resize changes keys keys = mp->ma_keys; } const Py_ssize_t hashpos = find_empty_slot(keys, hash); const Py_ssize_t dk_nentries = keys->dk_nentries; PyDictKeyEntry* ep = &DK_ENTRIES(keys)[dk_nentries]; dictkeys_set_index(keys, hashpos, dk_nentries); ep->me_key = key; ep->me_hash = hash; ep->me_value = value; mp->ma_used++; keys->dk_usable--; keys->dk_nentries++; assert(keys->dk_usable >= 0); } else { DK_ENTRIES(mp->ma_keys)[ix].me_value = value; Py_DECREF(old_value); /* which **CAN** re-enter (see issue #22653) */ Py_DECREF(key); } ASSERT_CONSISTENT(mp); return 0; } static int frozendict_setitem(PyObject *op, PyObject *key, PyObject *value, int empty) { Py_hash_t hash; assert(key); assert(value); if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) { return -1; } } return frozendict_insert((PyDictObject*) op, key, hash, value, empty); } // int _PyFrozendict_SetItem(PyObject *op, // PyObject *key, // PyObject *value, // int empty) { // if (! PyAnyFrozenDict_Check(op)) { // PyErr_BadInternalCall(); // return -1; // } // return frozendict_setitem(op, key, value, empty); // } static PyObject* _frozendict_new( PyTypeObject* type, PyObject* args, PyObject* kwds, const int use_empty_frozendict ); static PyObject * frozendict_fromkeys_impl(PyTypeObject *type, PyObject *iterable, PyObject *value) { PyObject *it; /* iter(iterable) */ PyObject *key; PyObject *d; int status; d = _frozendict_new(&PyFrozenDict_Type, NULL, NULL, 0); if (d == NULL) return NULL; Py_ssize_t size; PyDictObject *mp = (PyDictObject *)d; mp->ma_keys = new_keys_object(PyDict_MINSIZE); if (PyAnyDict_CheckExact(iterable)) { PyObject *oldvalue; Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; size = PyDict_GET_SIZE(iterable); if (mp->ma_keys->dk_usable < size) { if (frozendict_resize(mp, estimate_keysize(size))) { Py_DECREF(d); return NULL; } } while (_d_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) { if (frozendict_insert(mp, key, hash, value, 0)) { Py_DECREF(d); return NULL; } } return d; } else if (PyAnySet_CheckExact(iterable)) { Py_ssize_t pos = 0; PyObject *key; Py_hash_t hash; size = PySet_GET_SIZE(iterable); if (mp->ma_keys->dk_usable < size) { if (frozendict_resize(mp, estimate_keysize(size))) { Py_DECREF(d); return NULL; } } while (_PySet_NextEntry(iterable, &pos, &key, &hash)) { if (frozendict_insert(mp, key, hash, value, 0)) { Py_DECREF(d); return NULL; } } } else { it = PyObject_GetIter(iterable); if (it == NULL){ Py_DECREF(d); return NULL; } while ((key = PyIter_Next(it)) != NULL) { status = frozendict_setitem(d, key, value, 0); Py_DECREF(key); if (status < 0) { Py_DECREF(it); Py_DECREF(d); return NULL; } } Py_DECREF(it); if (PyErr_Occurred()) { Py_DECREF(d); return NULL; } } ASSERT_CONSISTENT(mp); if (type == &PyFrozenDict_Type) { return d; } PyObject* args = PyTuple_New(1); if (args == NULL) { Py_DECREF(d); return NULL; } PyTuple_SET_ITEM(args, 0, d); PyObject* res = PyObject_Call((PyObject*) type, args, NULL); Py_DECREF(args); return res; } /* Methods */ #define REPR_GENERIC_START "(" #define REPR_GENERIC_END ")" #define REPR_GENERIC_START_LEN 1 #define REPR_GENERIC_END_LEN 1 static PyObject* frozendict_repr(PyFrozenDictObject* mp) { PyObject* dict_repr_res = dict_repr((PyDictObject*) mp); if (dict_repr_res == NULL) { return NULL; } _PyUnicodeWriter writer; _PyUnicodeWriter_Init(&writer); int error = 0; PyObject* o = (PyObject*) mp; Py_ReprEnter(o); PyTypeObject* type = Py_TYPE(mp); size_t frozendict_name_len = strlen(type->tp_name); writer.min_length = ( frozendict_name_len + REPR_GENERIC_START_LEN + PyObject_Length(dict_repr_res) + REPR_GENERIC_END_LEN ); if (_PyUnicodeWriter_WriteASCIIString( &writer, type->tp_name, frozendict_name_len )) { error = 1; } else { if (_PyUnicodeWriter_WriteASCIIString( &writer, REPR_GENERIC_START, REPR_GENERIC_START_LEN )) { error = 1; } else { if (_PyUnicodeWriter_WriteStr(&writer, dict_repr_res)) { error = 1; } else { error = _PyUnicodeWriter_WriteASCIIString( &writer, REPR_GENERIC_END, REPR_GENERIC_END_LEN ); } } } Py_ReprLeave(o); Py_DECREF(dict_repr_res); if (error) { _PyUnicodeWriter_Dealloc(&writer); return NULL; } return _PyUnicodeWriter_Finish(&writer); } static PyMappingMethods frozendict_as_mapping = { (lenfunc)dict_length, /*mp_length*/ (binaryfunc)dict_subscript, /*mp_subscript*/ }; static int frozendict_merge(PyObject* a, PyObject* b, int empty) { /* We accept for the argument either a concrete dictionary object, * or an abstract "mapping" object. For the former, we can do * things quite efficiently. For the latter, we only require that * PyMapping_Keys() and PyObject_GetItem() be supported. */ assert(a != NULL); assert(PyAnyFrozenDict_Check(a)); assert(b != NULL); PyDictObject* mp = (PyDictObject*) a; if ( PyAnyDict_Check(b) && ( Py_TYPE(b)->tp_iter == PyDict_Type.tp_iter || Py_TYPE(b)->tp_iter == (getiterfunc)frozendict_iter ) ) { PyDictObject* other = (PyDictObject*)b; const Py_ssize_t numentries = other->ma_used; if (other == mp || numentries == 0) { /* a.update(a) or a.update({}); nothing to do */ return 0; } const int is_other_combined = other->ma_values == NULL; PyDictKeysObject* okeys = other->ma_keys; if ( empty && is_other_combined && numentries == okeys->dk_nentries ) { PyDictKeysObject *keys = clone_combined_dict_keys(other); if (keys == NULL) { return -1; } mp->ma_keys = keys; mp->ma_used = numentries; mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(mp); if (_PyObject_GC_IS_TRACKED(other) && !_PyObject_GC_IS_TRACKED(mp)) { PyObject_GC_Track(mp); } return 0; } PyDictKeyEntry* ep0 = DK_ENTRIES(okeys); PyDictKeyEntry* entry; PyObject* key; PyObject* value; Py_hash_t hash; int err; if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } /* Do one big resize at the start, rather than * incrementally resizing as we insert new items. Expect * that there will be no (or few) overlapping keys. */ if (mp->ma_keys->dk_usable < numentries) { if (frozendict_resize(mp, estimate_keysize(mp->ma_used + numentries))) { return -1; } } for (Py_ssize_t i = 0, n = okeys->dk_nentries; i < n; i++) { entry = &ep0[i]; key = entry->me_key; hash = entry->me_hash; if (is_other_combined) { value = entry->me_value; } else { value = other->ma_values[i]; } if (value != NULL) { Py_INCREF(key); Py_INCREF(value); err = frozendict_insert(mp, key, hash, value, empty); Py_DECREF(value); Py_DECREF(key); if (err != 0) { return -1; } if (n != other->ma_keys->dk_nentries) { PyErr_SetString(PyExc_RuntimeError, "dict mutated during update"); return -1; } } } } else { /* Do it the generic, slower way */ PyObject *keys = PyMapping_Keys(b); PyObject *iter; PyObject *key, *value; int status; if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } if (keys == NULL) /* Docstring says this is equivalent to E.keys() so * if E doesn't have a .keys() method we want * AttributeError to percolate up. Might as well * do the same for any other error. */ return -1; iter = PyObject_GetIter(keys); Py_DECREF(keys); if (iter == NULL) return -1; for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) { value = PyObject_GetItem(b, key); if (value == NULL) { Py_DECREF(iter); Py_DECREF(key); return -1; } status = frozendict_setitem(a, key, value, 0); Py_DECREF(key); Py_DECREF(value); if (status < 0) { Py_DECREF(iter); return -1; } } Py_DECREF(iter); if (PyErr_Occurred()) /* Iterator completed, via error */ return -1; } ASSERT_CONSISTENT(a); return 0; } static int frozendict_merge_from_seq2(PyObject* d, PyObject* seq2) { assert(d != NULL); assert(PyAnyFrozenDict_Check(d)); assert(seq2 != NULL); PyObject* it = PyObject_GetIter(seq2); if (it == NULL) { return -1; } PyObject* fast; /* item as a 2-tuple or 2-list */ PyObject* key = NULL; PyObject* value = NULL; Py_ssize_t n; PyObject* item; int res = 0; PyDictObject* mp = (PyDictObject*) d; if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } for (Py_ssize_t i = 0; ; ++i) { fast = NULL; item = PyIter_Next(it); if (item == NULL) { if (PyErr_Occurred()) { res = -1; } break; } /* Convert item to sequence, and verify length 2. */ fast = PySequence_Fast(item, ""); if (fast == NULL) { if (PyErr_ExceptionMatches(PyExc_TypeError)) { PyErr_Format(PyExc_TypeError, "cannot convert dictionary update " "sequence element #%zd to a sequence", i); } Py_DECREF(item); res = -1; break; } n = PySequence_Fast_GET_SIZE(fast); if (n != 2) { PyErr_Format(PyExc_ValueError, "dictionary update sequence element #%zd " "has length %zd; 2 is required", i, n); Py_DECREF(fast); Py_DECREF(item); res = -1; break; } /* Update/merge with this (key, value) pair. */ key = PySequence_Fast_GET_ITEM(fast, 0); Py_INCREF(key); value = PySequence_Fast_GET_ITEM(fast, 1); Py_INCREF(value); if (frozendict_setitem(d, key, value, 0) < 0) { Py_DECREF(key); Py_DECREF(value); Py_DECREF(fast); Py_DECREF(item); res = -1; break; } Py_DECREF(key); Py_DECREF(value); Py_DECREF(fast); Py_DECREF(item); } Py_DECREF(it); ASSERT_CONSISTENT(d); return res; } static int frozendict_update_arg(PyObject *self, PyObject *arg, const int empty) { if (PyAnyDict_CheckExact(arg)) { return frozendict_merge(self, arg, empty); } _Py_IDENTIFIER(keys); PyObject *func; if (_PyObject_LookupAttrId(arg, &PyId_keys, &func) < 0) { return -1; } if (func != NULL) { Py_DECREF(func); return frozendict_merge(self, arg, empty); } return frozendict_merge_from_seq2(self, arg); } static int frozendict_update_common(PyObject* self, PyObject* arg, PyObject* kwds) { int result = 0; const int no_arg = (arg == NULL); if (! no_arg) { result = frozendict_update_arg(self, arg, 1); } if (result == 0 && kwds != NULL) { if (PyArg_ValidateKeywordArguments(kwds)) { result = frozendict_merge(self, kwds, no_arg); } else { result = -1; } } return result; } /* Forward */ static PyObject *frozendictkeys_new(PyObject *, PyObject *); static PyObject *frozendictitems_new(PyObject *, PyObject *); static PyObject *frozendictvalues_new(PyObject *, PyObject *); #define MINUSONE_HASH ((Py_hash_t) -1) static Py_hash_t frozendict_hash(PyObject* self) { PyFrozenDictObject* frozen_self = (PyFrozenDictObject*) self; Py_hash_t hash; if (frozen_self->ma_hash != MINUSONE_HASH) { hash = frozen_self->ma_hash; } else { PyObject* frozen_items_tmp = frozendictitems_new(self, NULL); int save_hash = 1; if (frozen_items_tmp == NULL) { hash = MINUSONE_HASH; save_hash = 0; } else { PyObject* frozen_items = PyFrozenSet_New(frozen_items_tmp); Py_DECREF(frozen_items_tmp); if (frozen_items == NULL) { hash = MINUSONE_HASH; save_hash = 0; } else { hash = PyFrozenSet_Type.tp_hash(frozen_items); Py_DECREF(frozen_items); } } if (save_hash) { frozen_self->ma_hash = hash; } } return hash; } static PyObject* frozendict_copy(PyObject* o, PyObject* Py_UNUSED(ignored)) { if (PyAnyFrozenDict_CheckExact(o)) { Py_INCREF(o); return o; } PyObject* args = PyTuple_New(1); if (args == NULL) { return NULL; } Py_INCREF(o); PyTuple_SET_ITEM(args, 0, o); PyTypeObject* type = Py_TYPE(o); PyObject* res = PyObject_Call((PyObject *) type, args, NULL); Py_DECREF(args); return res; } PyObject* frozendict_deepcopy(PyObject* self, PyObject* memo) { if (PyAnyFrozenDict_CheckExact(self)) { frozendict_hash(self); if (PyErr_Occurred()) { PyErr_Clear(); } else { Py_INCREF(self); return self; } } if (! PyAnyFrozenDict_Check(self)) { Py_RETURN_NOTIMPLEMENTED; } PyObject* d = PyDict_New(); if (d == NULL) { return NULL; } PyObject* copy_module_name = NULL; PyObject* copy_module = NULL; PyObject* deepcopy_fun = NULL; PyObject* deep_args = NULL; PyObject* deep_d = NULL; PyObject* args = NULL; PyObject* res = NULL; int decref_d = 1; int decref_deep_d = 1; if (PyDict_Merge(d, self, 1)) { goto end; } copy_module_name = PyUnicode_FromString("copy"); if (copy_module_name == NULL) { goto end; } copy_module = PyImport_Import(copy_module_name); if (copy_module == NULL) { goto end; } deepcopy_fun = PyObject_GetAttrString(copy_module, "deepcopy"); if (deepcopy_fun == NULL) { goto end; } deep_args = PyTuple_New(2); if (deep_args == NULL) { goto end; } PyTuple_SET_ITEM(deep_args, 0, d); decref_d = 0; Py_INCREF(memo); PyTuple_SET_ITEM(deep_args, 1, memo); deep_d = PyObject_CallObject(deepcopy_fun, deep_args); if (deep_d == NULL) { goto end; } args = PyTuple_New(1); if (args == NULL) { goto end; } PyTuple_SET_ITEM(args, 0, deep_d); decref_deep_d = 0; PyTypeObject* type = Py_TYPE(self); res = PyObject_Call((PyObject *) type, args, NULL); end: Py_XDECREF(args); Py_XDECREF(deep_args); Py_XDECREF(deepcopy_fun); Py_XDECREF(copy_module); Py_XDECREF(copy_module_name); if (decref_d) { Py_DECREF(d); } if (decref_deep_d) { Py_DECREF(deep_d); } return res; } static int frozendict_equal(PyDictObject* a, PyDictObject* b) { if (a == b) { return 1; } if (a->ma_used != b->ma_used) { /* can't be equal if # of entries differ */ return 0; } PyDictKeysObject* keys = a->ma_keys; PyDictKeyEntry* ep; PyObject* aval; int cmp = 1; PyObject* bval; PyObject* key; /* Same # of entries -- check all of 'em. Exit early on any diff. */ for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) { ep = &DK_ENTRIES(keys)[i]; aval = ep->me_value; Py_INCREF(aval); key = ep->me_key; Py_INCREF(key); /* reuse the known hash value */ b->ma_keys->dk_lookup(b, key, ep->me_hash, &bval); if (bval == NULL) { if (PyErr_Occurred()) { cmp = -1; } else { cmp = 0; } } else { Py_INCREF(bval); cmp = PyObject_RichCompareBool(aval, bval, Py_EQ); Py_DECREF(bval); } Py_DECREF(key); Py_DECREF(aval); if (cmp <= 0) { /* error or not equal */ break; } } return cmp; } static Py_ssize_t dict_get_index(PyDictObject *self, PyObject *key) { Py_hash_t hash; PyObject* val; if (!PyUnicode_CheckExact(key) || (hash = ((PyASCIIObject *) key)->hash) == -1) { hash = PyObject_Hash(key); if (hash == -1) return DKIX_ERROR; } return (self->ma_keys->dk_lookup) (self, key, hash, &val); } static PyObject * frozendict_reduce(PyFrozenDictObject* mp, PyObject *Py_UNUSED(ignored)) { PyObject *d = PyDict_New(); if (d == NULL) { return NULL; } if (PyDict_Merge(d, (PyObject *)mp, 1)) { Py_DECREF(d); return NULL; } return Py_BuildValue("O(N)", Py_TYPE(mp), d); } static PyObject* frozendict_clone(PyObject* self) { PyTypeObject* type = Py_TYPE(self); PyObject* new_op = type->tp_alloc(type, 0); if (new_op == NULL){ return NULL; } if (type == &PyFrozenDict_Type) { PyObject_GC_UnTrack(new_op); } PyDictObject* mp = (PyDictObject*) self; PyDictKeysObject *keys = clone_combined_dict_keys(mp); if (keys == NULL) { return NULL; } PyFrozenDictObject* new_mp = (PyFrozenDictObject*) new_op; new_mp->ma_keys = keys; if (_PyObject_GC_IS_TRACKED(mp) && !_PyObject_GC_IS_TRACKED(new_mp)) { PyObject_GC_Track(new_mp); } new_mp->ma_used = mp->ma_used; new_mp->ma_hash = MINUSONE_HASH; new_mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(new_mp); return new_op; } static PyObject* frozendict_set( PyObject* self, PyObject* const* args, Py_ssize_t nargs ) { if (!_PyArg_CheckPositional("set", nargs, 2, 2)) { return NULL; } PyObject* new_op = frozendict_clone(self); if (new_op == NULL) { return NULL; } PyObject* set_key = args[0]; if (frozendict_setitem(new_op, set_key, args[1], 0)) { Py_DECREF(new_op); return NULL; } if ( ((PyDictObject*) self)->ma_keys->dk_lookup == lookdict_unicode_nodummy && ! PyUnicode_CheckExact(set_key) ) { ((PyFrozenDictObject*) new_op)->ma_keys->dk_lookup = lookdict; } return new_op; } static PyObject* frozendict_setdefault( PyObject* self, PyObject* const* args, Py_ssize_t nargs ) { if (!_PyArg_CheckPositional("setdefault", nargs, 1, 2)) { return NULL; } PyObject* set_key = args[0]; if (PyDict_Contains(self, set_key)) { Py_INCREF(self); return self; } PyObject* new_op = frozendict_clone(self); if (new_op == NULL) { return NULL; } PyObject* val; if (nargs == 2) { val = args[1]; } else { val = Py_None; } if (frozendict_setitem(new_op, set_key, val, 0)) { Py_DECREF(new_op); return NULL; } if ( ((PyDictObject*) self)->ma_keys->dk_lookup == lookdict_unicode_nodummy && ! PyUnicode_CheckExact(set_key) ) { ((PyFrozenDictObject*) new_op)->ma_keys->dk_lookup = lookdict; } return new_op; } static PyObject* frozendict_delete( PyObject* self, PyObject *const *args, Py_ssize_t nargs ) { if (! _PyArg_CheckPositional("delete", nargs, 1, 1)) { return NULL; } PyDictObject* mp = (PyDictObject*) self; PyObject* del_key = args[0]; const Py_ssize_t ix = dict_get_index(mp, del_key); if (ix == DKIX_ERROR) { return NULL; } if (ix == DKIX_EMPTY) { _PyErr_SetKeyError(del_key); return NULL; } const Py_ssize_t size = mp->ma_used; const Py_ssize_t sizemm = size - 1; if (sizemm == 0) { PyObject* args = PyTuple_New(0); if (args == NULL) { return NULL; } return PyObject_Call((PyObject*) Py_TYPE(self), args, NULL); } PyTypeObject* type = Py_TYPE(self); PyObject* new_op = type->tp_alloc(type, 0); if (new_op == NULL) { return NULL; } if (type == &PyFrozenDict_Type) { PyObject_GC_UnTrack(new_op); } const Py_ssize_t newsize = estimate_keysize(sizemm); if (newsize <= 0) { Py_DECREF(new_op); PyErr_NoMemory(); return NULL; } assert(IS_POWER_OF_2(newsize)); assert(newsize >= PyDict_MINSIZE); /* Allocate a new table. */ PyDictKeysObject* new_keys = new_keys_object(newsize); if (new_keys == NULL) { Py_DECREF(new_op); return NULL; } const PyDictKeysObject* old_keys = mp->ma_keys; new_keys->dk_lookup = old_keys->dk_lookup; PyFrozenDictObject* new_mp = (PyFrozenDictObject*) new_op; // New table must be large enough. assert(new_keys->dk_usable >= new_mp->ma_used); new_mp->ma_keys = new_keys; new_mp->ma_hash = MINUSONE_HASH; new_mp->ma_version_tag = DICT_NEXT_VERSION(); PyObject* key; PyObject* value; Py_hash_t hash; Py_ssize_t hashpos; PyDictKeyEntry* old_entries = DK_ENTRIES(old_keys); PyDictKeyEntry* new_entries = DK_ENTRIES(new_keys); PyDictKeyEntry* old_entry; PyDictKeyEntry* new_entry; Py_ssize_t new_i; int deleted = 0; for (Py_ssize_t i = 0; i < size; i++) { if (i == ix) { deleted = 1; continue; } new_i = i - deleted; old_entry = &old_entries[i]; hash = old_entry->me_hash; key = old_entry->me_key; value = old_entry->me_value; Py_INCREF(key); Py_INCREF(value); hashpos = find_empty_slot(new_keys, hash); dictkeys_set_index(new_keys, hashpos, new_i); new_entry = &new_entries[new_i]; new_entry->me_key = key; new_entry->me_hash = hash; new_entry->me_value = value; } new_mp->ma_used = sizemm; new_keys->dk_usable -= sizemm; new_keys->dk_nentries = sizemm; ASSERT_CONSISTENT(new_mp); return new_op; } static const PyObject* frozendict_key( PyObject* self, PyObject *const *args, Py_ssize_t nargs ) { if (! _PyArg_CheckPositional("key", nargs, 0, 1)) { return NULL; } Py_ssize_t index; Py_ssize_t passed_index; PyDictObject* d = (PyDictObject*) self; const Py_ssize_t size = d->ma_used; if (nargs > 0) { index = PyLong_AsSsize_t(args[0]); passed_index = index; if (index < 0) { if (PyErr_Occurred()) { return NULL; } index += size; } } else { index = 0; passed_index = index; } const Py_ssize_t maxindex = size - 1; if (index > maxindex || index < 0) { PyErr_Format( PyExc_IndexError, "%s index %zd out of range %zd", Py_TYPE(self)->tp_name, passed_index, maxindex ); return NULL; } const PyObject* res = DK_ENTRIES(d->ma_keys)[index].me_key; Py_INCREF(res); return res; } static const PyObject* frozendict_value( PyObject* self, PyObject *const *args, Py_ssize_t nargs ) { if (! _PyArg_CheckPositional("value", nargs, 0, 1)) { return NULL; } Py_ssize_t index; Py_ssize_t passed_index; PyDictObject* d = (PyDictObject*) self; const Py_ssize_t size = d->ma_used; if (nargs > 0) { index = PyLong_AsSsize_t(args[0]); passed_index = index; if (index < 0) { if (PyErr_Occurred()) { return NULL; } index += size; } } else { index = 0; passed_index = index; } const Py_ssize_t maxindex = size - 1; if (index > maxindex || index < 0) { PyErr_Format( PyExc_IndexError, "%s index %zd out of range %zd", Py_TYPE(self)->tp_name, passed_index, maxindex ); return NULL; } const PyObject* res = DK_ENTRIES(d->ma_keys)[index].me_value; Py_INCREF(res); return res; } static const PyObject* frozendict_item( PyObject* self, PyObject *const *args, Py_ssize_t nargs ) { if (! _PyArg_CheckPositional("item", nargs, 0, 1)) { return NULL; } Py_ssize_t index; Py_ssize_t passed_index; PyDictObject* d = (PyDictObject*) self; const Py_ssize_t size = d->ma_used; if (nargs > 0) { index = PyLong_AsSsize_t(args[0]); passed_index = index; if (index < 0) { if (PyErr_Occurred()) { return NULL; } index += size; } } else { index = 0; passed_index = index; } const Py_ssize_t maxindex = size - 1; if (index > maxindex || index < 0) { PyErr_Format( PyExc_IndexError, "%s index %zd out of range %zd", Py_TYPE(self)->tp_name, passed_index, maxindex ); return NULL; } PyObject* key = DK_ENTRIES(d->ma_keys)[index].me_key; Py_INCREF(key); PyObject* val = DK_ENTRIES(d->ma_keys)[index].me_value; Py_INCREF(val); const PyObject* res = PyTuple_New(2); PyTuple_SET_ITEM(res, 0, key); PyTuple_SET_ITEM(res, 1, val); return res; } PyDoc_STRVAR(frozendict_set_doc, "set($self, key, value, /)\n" "--\n" "\n" "Returns a copy of the dictionary with the new (key, value) item. "); PyDoc_STRVAR(frozendict_setdefault_doc, "set($self, key[, default], /)\n" "--\n" "\n" "If key is in the dictionary, it returns the dictionary unchanged. \n" "Otherwise, it returns a copy of the dictionary with the new (key, default) item; \n" "default argument is optional and is None by default. "); PyDoc_STRVAR(frozendict_delete_doc, "delete($self, key, /)\n" "--\n" "\n" "Returns a copy of the dictionary without the item of the corresponding key. "); PyDoc_STRVAR(frozendict_key_doc, "key($self[, index], /)\n" "--\n" "\n" "Get the key at the specified index (insertion order). If index is not \n" "passed, it defaults to 0. If index is negative, returns the key at \n" "position size + index. "); PyDoc_STRVAR(frozendict_value_doc, "value($self[, index], /)\n" "--\n" "\n" "Get the value at the specified index (insertion order). If index is not \n" "passed, it defaults to 0. If index is negative, returns the value at \n" "position size + index. "); PyDoc_STRVAR(frozendict_item_doc, "item($self[, index], /)\n" "--\n" "\n" "Get the (key, value) item at the specified index (insertion order). If \n" "index is not passed, it defaults to 0. If index is negative, returns the \n" "item at position size + index. "); static PyMethodDef frozendict_mapp_methods[] = { DICT___CONTAINS___METHODDEF {"__getitem__", (PyCFunction)(void(*)(void))dict_subscript, METH_O | METH_COEXIST, getitem__doc__}, {"__sizeof__", (PyCFunction)(void(*)(void))dict_sizeof, METH_NOARGS, sizeof__doc__}, DICT_GET_METHODDEF {"keys", frozendictkeys_new, METH_NOARGS, keys__doc__}, {"items", frozendictitems_new, METH_NOARGS, items__doc__}, {"values", frozendictvalues_new, METH_NOARGS, values__doc__}, {"fromkeys", (PyCFunction)(void(*)(void))dict_fromkeys, METH_FASTCALL|METH_CLASS, dict_fromkeys__doc__}, {"copy", (PyCFunction)frozendict_copy, METH_NOARGS, copy__doc__}, {"__copy__", (PyCFunction)frozendict_copy, METH_NOARGS, "Returns a copy of the object."}, {"__deepcopy__", (PyCFunction)frozendict_deepcopy, METH_O, "Returns a deepcopy of the object."}, DICT___REVERSED___METHODDEF {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, "See PEP 585"}, {"__reduce__", (PyCFunction)(void(*)(void))frozendict_reduce, METH_NOARGS, ""}, {"set", (PyCFunction)(void(*)(void)) frozendict_set, METH_FASTCALL, frozendict_set_doc}, {"setdefault", (PyCFunction)(void(*)(void)) frozendict_setdefault, METH_FASTCALL, frozendict_setdefault_doc}, {"delete", (PyCFunction)(void(*)(void)) frozendict_delete, METH_FASTCALL, frozendict_delete_doc}, {"key", (PyCFunction)(void(*)(void)) frozendict_key, METH_FASTCALL, frozendict_key_doc}, {"value", (PyCFunction)(void(*)(void)) frozendict_value, METH_FASTCALL, frozendict_value_doc}, {"item", (PyCFunction)(void(*)(void)) frozendict_item, METH_FASTCALL, frozendict_item_doc}, {NULL, NULL} /* sentinel */ }; static PyObject* frozendict_new_barebone(PyTypeObject* type) { PyObject* self = type->tp_alloc(type, 0); if (self == NULL) { return NULL; } /* The object has been implicitly tracked by tp_alloc */ if (type == &PyFrozenDict_Type) { PyObject_GC_UnTrack(self); } PyFrozenDictObject* mp = (PyFrozenDictObject*) self; mp->ma_keys = NULL; mp->ma_values = NULL; mp->ma_used = 0; mp->ma_hash = MINUSONE_HASH; return self; } // empty frozendict singleton static PyObject* empty_frozendict = NULL; // if frozendict is empty, return the empty singleton static PyObject* frozendict_create_empty( PyFrozenDictObject* mp, const PyTypeObject* type, const int use_empty_frozendict ) { if (mp->ma_used == 0) { if (use_empty_frozendict && type == &PyFrozenDict_Type) { if (empty_frozendict == NULL) { empty_frozendict = (PyObject*) mp; Py_INCREF(Py_EMPTY_KEYS); ((PyDictObject*) empty_frozendict)->ma_keys = Py_EMPTY_KEYS; mp->ma_version_tag = DICT_NEXT_VERSION(); } else { Py_DECREF(mp); } Py_INCREF(empty_frozendict); return empty_frozendict; } else { if (mp->ma_keys != NULL) { frozendict_keys_decref(mp->ma_keys, 0); } Py_INCREF(Py_EMPTY_KEYS); mp->ma_keys = Py_EMPTY_KEYS; return NULL; } } return NULL; } static PyObject* frozendict_vectorcall(PyObject* type, PyObject* const* args, size_t nargsf, PyObject* kwnames) { assert(PyType_Check(type)); Py_ssize_t nargs = PyVectorcall_NARGS(nargsf); if (! _PyArg_CheckPositional("dict", nargs, 0, 1)) { return NULL; } PyTypeObject* ttype = (PyTypeObject*) type; Py_ssize_t size; PyObject* arg = NULL; if (nargs == 1) { arg = args[0]; // only argument is a frozendict if ( arg != NULL && PyAnyFrozenDict_CheckExact(arg) && ttype == &PyFrozenDict_Type ) { size = ( kwnames == NULL ? 0 : PyTuple_GET_SIZE(kwnames) ); if (size == 0) { Py_INCREF(arg); return arg; } } } PyObject* self = frozendict_new_barebone(ttype); PyFrozenDictObject* mp = (PyFrozenDictObject*) self; int empty = 1; if (nargs == 1) { empty = 0; if (frozendict_update_arg(self, arg, 1) < 0) { Py_DECREF(self); return NULL; } args++; } if (kwnames != NULL) { if (mp->ma_keys == NULL) { mp->ma_keys = new_keys_object(PyDict_MINSIZE); } size = ( kwnames == NULL ? 0 : PyTuple_GET_SIZE(kwnames) ); if (mp->ma_keys->dk_usable < size) { if (frozendict_resize((PyDictObject*) self, estimate_keysize(mp->ma_used + size))) { return NULL; } } for (Py_ssize_t i = 0; i < size; i++) { if (frozendict_setitem(self, PyTuple_GET_ITEM(kwnames, i), args[i], empty) < 0) { Py_DECREF(self); return NULL; } } } PyObject* self_empty = frozendict_create_empty(mp, ttype, 1); if (self_empty != NULL) { return self_empty; } mp->ma_version_tag = DICT_NEXT_VERSION(); ASSERT_CONSISTENT(mp); return self; } static PyObject* _frozendict_new( PyTypeObject* type, PyObject* args, PyObject* kwds, const int use_empty_frozendict ) { assert(type != NULL && type->tp_alloc != NULL); PyObject* arg = NULL; if (args != NULL && ! PyArg_UnpackTuple(args, "dict", 0, 1, &arg)) { return NULL; } const int arg_is_frozendict = (arg != NULL && PyAnyFrozenDict_CheckExact(arg)); const int kwds_size = ((kwds != NULL) ? ((PyDictObject*) kwds)->ma_used : 0 ); // only argument is a frozendict if (arg_is_frozendict && kwds_size == 0 && type == &PyFrozenDict_Type) { Py_INCREF(arg); return arg; } PyObject* self = frozendict_new_barebone(type); PyFrozenDictObject* mp = (PyFrozenDictObject*) self; if (frozendict_update_common(self, arg, kwds)) { Py_DECREF(self); return NULL; } PyObject* empty = frozendict_create_empty(mp, type, use_empty_frozendict); if (empty != NULL) { return empty; } mp->ma_version_tag = DICT_NEXT_VERSION(); return self; } static PyObject* frozendict_new(PyTypeObject* type, PyObject* args, PyObject* kwds) { return _frozendict_new(type, args, kwds, 1); } static PyObject* frozendict_or(PyObject *self, PyObject *other) { if (! PyAnyFrozenDict_Check(self) || ! PyAnyDict_Check(other)) { Py_RETURN_NOTIMPLEMENTED; } PyObject* new = frozendict_clone(self); if (new == NULL) { return NULL; } if (frozendict_update_arg(new, other, 0)) { Py_DECREF(new); return NULL; } return new; } static PyNumberMethods frozendict_as_number = { .nb_or = frozendict_or, }; #define FROZENDICT_CLASS_NAME "frozendict" #define FROZENDICT_MODULE_NAME "frozendict" #define FROZENDICT_FULL_NAME FROZENDICT_MODULE_NAME "." FROZENDICT_CLASS_NAME PyDoc_STRVAR(frozendict_doc, "An immutable version of dict.\n" "\n" FROZENDICT_FULL_NAME "() -> returns an empty immutable dictionary\n" FROZENDICT_FULL_NAME "(mapping) -> returns an immutable dictionary initialized from a mapping object's\n" " (key, value) pairs\n" FROZENDICT_FULL_NAME "(iterable) -> returns an immutable dictionary, equivalent to:\n" " d = {}\n" " " " for k, v in iterable:\n" " d[k] = v\n" " " " " FROZENDICT_FULL_NAME "(d)\n" FROZENDICT_FULL_NAME "(**kwargs) -> returns an immutable dictionary initialized with the name=value pairs\n" " in the keyword argument list. For example: " FROZENDICT_FULL_NAME "(one=1, two=2)"); static PyTypeObject PyFrozenDict_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_FULL_NAME, /* tp_name */ sizeof(PyFrozenDictObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)dict_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)frozendict_repr, /* tp_repr */ &frozendict_as_number, /* tp_as_number */ &dict_as_sequence, /* tp_as_sequence */ &frozendict_as_mapping, /* tp_as_mapping */ (hashfunc)frozendict_hash, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /* tp_flags */ frozendict_doc, /* tp_doc */ dict_traverse, /* tp_traverse */ 0, /* tp_clear */ dict_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendict_iter, /* tp_iter */ 0, /* tp_iternext */ frozendict_mapp_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ PyType_GenericAlloc, /* tp_alloc */ frozendict_new, /* tp_new */ PyObject_GC_Del, /* tp_free */ .tp_vectorcall = frozendict_vectorcall, }; /* Dictionary iterator types */ static PyObject* frozendict_iter(PyDictObject *dict) { return dictiter_new(dict, &PyFrozenDictIterKey_Type); } static PyObject* frozendictiter_iternextkey(dictiterobject* di) { Py_ssize_t pos = di->di_pos; assert(pos >= 0); PyDictObject* d = di->di_dict; if (d == NULL) { return NULL; } assert(PyAnyFrozenDict_Check(d)); if (pos >= d->ma_used) { di->di_dict = NULL; Py_DECREF(d); return NULL; } PyObject* key = DK_ENTRIES(d->ma_keys)[pos].me_key; assert(key != NULL); di->di_pos++; di->len--; Py_INCREF(key); return key; } static PyTypeObject PyFrozenDictIterKey_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".keyiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)frozendictiter_iternextkey, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject* frozendictiter_iternextvalue(dictiterobject* di) { Py_ssize_t pos = di->di_pos; assert(pos >= 0); PyDictObject* d = di->di_dict; if (d == NULL) { return NULL; } assert(PyAnyFrozenDict_Check(d)); if (pos >= d->ma_used) { di->di_dict = NULL; Py_DECREF(d); return NULL; } PyObject* val = DK_ENTRIES(d->ma_keys)[pos].me_value; assert(val != NULL); di->di_pos++; di->len--; Py_INCREF(val); return val; } static PyTypeObject PyFrozenDictIterValue_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".valueiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)frozendictiter_iternextvalue, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; static PyObject* frozendictiter_iternextitem(dictiterobject* di) { Py_ssize_t pos = di->di_pos; assert(pos >= 0); PyDictObject* d = di->di_dict; if (d == NULL) { return NULL; } assert(PyAnyFrozenDict_Check(d)); if (pos >= d->ma_used) { di->di_dict = NULL; Py_DECREF(d); return NULL; } PyDictKeyEntry* entry_ptr = &DK_ENTRIES(d->ma_keys)[pos]; PyObject* key = entry_ptr->me_key; PyObject* val = entry_ptr->me_value; assert(key != NULL); assert(val != NULL); di->di_pos++; di->len--; Py_INCREF(key); Py_INCREF(val); PyObject* result; if (Py_REFCNT(di->di_result) == 1) { result = di->di_result; PyObject *oldkey = PyTuple_GET_ITEM(result, 0); PyObject *oldvalue = PyTuple_GET_ITEM(result, 1); Py_INCREF(result); Py_DECREF(oldkey); Py_DECREF(oldvalue); if (!_PyObject_GC_IS_TRACKED(result)) { PyObject_GC_Track(result); } } else { result = PyTuple_New(2); if (result == NULL) return NULL; } PyTuple_SET_ITEM(result, 0, key); /* steals reference */ PyTuple_SET_ITEM(result, 1, val); /* steals reference */ return result; } static PyTypeObject PyFrozenDictIterItem_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".itemiterator", /* tp_name */ sizeof(dictiterobject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictiter_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictiter_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ PyObject_SelfIter, /* tp_iter */ (iternextfunc)frozendictiter_iternextitem, /* tp_iternext */ dictiter_methods, /* tp_methods */ 0, }; /*** dict_keys ***/ static PyObject * frozendictkeys_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyFrozenDictIterKey_Type); } static PyTypeObject PyFrozenDictKeys_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".keys", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictkeys_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendictkeys_iter, /* tp_iter */ 0, /* tp_iternext */ dictkeys_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * frozendictkeys_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _d_PyDictView_New(dict, &PyFrozenDictKeys_Type); } /*** dict_items ***/ static PyObject * frozendictitems_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyFrozenDictIterItem_Type); } static PyTypeObject PyFrozenDictItems_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".items", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ &dictviews_as_number, /* tp_as_number */ &dictitems_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ dictview_richcompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendictitems_iter, /* tp_iter */ 0, /* tp_iternext */ dictitems_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * frozendictitems_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _d_PyDictView_New(dict, &PyFrozenDictItems_Type); } /*** dict_values ***/ static PyObject * frozendictvalues_iter(_PyDictViewObject *dv) { if (dv->dv_dict == NULL) { Py_RETURN_NONE; } return dictiter_new(dv->dv_dict, &PyFrozenDictIterValue_Type); } static PyTypeObject PyFrozenDictValues_Type = { PyVarObject_HEAD_INIT(NULL, 0) FROZENDICT_MODULE_NAME ".values", /* tp_name */ sizeof(_PyDictViewObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* methods */ (destructor)dictview_dealloc, /* tp_dealloc */ 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_as_async */ (reprfunc)dictview_repr, /* tp_repr */ 0, /* tp_as_number */ &dictvalues_as_sequence, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */ 0, /* tp_doc */ (traverseproc)dictview_traverse, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ (getiterfunc)frozendictvalues_iter, /* tp_iter */ 0, /* tp_iternext */ dictvalues_methods, /* tp_methods */ .tp_getset = dictview_getset, }; static PyObject * frozendictvalues_new(PyObject *dict, PyObject *Py_UNUSED(ignored)) { return _d_PyDictView_New(dict, &PyFrozenDictValues_Type); } static int frozendict_exec(PyObject *m) { /* Finalize the type object including setting type of the new type * object; doing it here is required for portability, too. */ if (PyType_Ready(&PyFrozenDict_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictIterKey_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictIterValue_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictIterItem_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictKeys_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictItems_Type) < 0) { goto fail; } if (PyType_Ready(&PyFrozenDictValues_Type) < 0) { goto fail; } PyModule_AddObject(m, FROZENDICT_CLASS_NAME, (PyObject *)&PyFrozenDict_Type); return 0; fail: Py_XDECREF(m); return -1; } static struct PyModuleDef_Slot frozendict_slots[] = { {Py_mod_exec, frozendict_exec}, {0, NULL}, }; static struct PyModuleDef frozendictmodule = { PyModuleDef_HEAD_INIT, FROZENDICT_MODULE_NAME, /* name of module */ NULL, /* module documentation, may be NULL */ 0, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ NULL, frozendict_slots, NULL, NULL, NULL }; PyMODINIT_FUNC PyInit__frozendict(void) { return PyModuleDef_Init(&frozendictmodule); } python-frozendict/src/frozendict/cool.py000066400000000000000000000206311470250572200210160ustar00rootroot00000000000000from types import MappingProxyType from array import array from frozendict import frozendict from collections.abc import MutableMapping, MutableSequence, MutableSet from enum import Enum # fix for python 3.9- # coverage does not work here! if not issubclass(array, MutableSequence): # pragma: no cover # noinspection PyUnresolvedReferences MutableSequence.register(array) def isIterableNotString(o): from collections import abc return ( isinstance(o, abc.Iterable) and not isinstance(o, memoryview) and not hasattr(o, "isalpha") ) def getItems(o): from collections import abc if not isinstance(o, abc.Iterable): raise TypeError("object must be an iterable") if isinstance(o, abc.Mapping): return dict.items return enumerate def nil(x): return x _freeze_conversion_map = frozendict({ MutableMapping: frozendict, bytearray: bytes, MutableSequence: tuple, MutableSet: frozenset, Enum: nil, }) _freeze_conversion_map_custom = {} class FreezeError(Exception): pass class FreezeWarning(UserWarning): pass def register(to_convert, converter, *, inverse = False): r""" Adds a `converter` for a type `to_convert`. `converter` must be callable. The new converter will be used by `deepfreeze()` and has precedence over any previous converter. If `to_covert` has already a converter, a FreezeWarning is raised. If `inverse` is True, the conversion is considered from an immutable type to a mutable one. This make it possible to convert mutable objects nested in the registered immutable one. """ if not issubclass(type(to_convert), type): raise ValueError( f"`to_convert` parameter must be a type, {to_convert} found" ) try: converter.__call__ except AttributeError: raise ValueError( f"`converter` parameter must be a callable, {converter}" + "found" ) if inverse: freeze_conversion_map = getFreezeConversionInverseMap() else: freeze_conversion_map = getFreezeConversionMap() if to_convert in freeze_conversion_map: import warnings warnings.warn( f"{to_convert.__name__} is already in the conversion map", FreezeWarning ) if inverse: freeze_conversion_map = _freeze_conversion_inverse_map_custom else: freeze_conversion_map = _freeze_conversion_map_custom freeze_conversion_map[to_convert] = converter def unregister(type, inverse = False): r""" Unregister a type from custom conversion. If `inverse` is `True`, the unregistered conversion is an inverse conversion (see `register()`). """ if inverse: freeze_conversion_map = _freeze_conversion_inverse_map_custom else: freeze_conversion_map = _freeze_conversion_map_custom try: del freeze_conversion_map[type] except KeyError: raise FreezeError(f"{type.__name__} is not registered") def getFreezeConversionMap(): return _freeze_conversion_map | _freeze_conversion_map_custom _freeze_conversion_inverse_map = frozendict({ frozendict: dict, MappingProxyType: dict, tuple: list, }) _freeze_conversion_inverse_map_custom = {} def getFreezeConversionInverseMap(): return ( _freeze_conversion_inverse_map | _freeze_conversion_inverse_map_custom ) _freeze_types = ( [x for x in _freeze_conversion_map] + [x for x in _freeze_conversion_inverse_map] ) def getFreezeTypes(): return (tuple( _freeze_types + [x for x in _freeze_conversion_map_custom] + [x for x in _freeze_conversion_inverse_map_custom] )) _freeze_types_plain = (MutableSet, bytearray, array) def deepfreeze( o, custom_converters = None, custom_inverse_converters = None ): r""" Converts the object and all the objects nested in it in its immutable counterparts. The conversion map is in getFreezeConversionMap(). You can register a new conversion using `register()` You can also pass a map of custom converters with `custom_converters` and a map of custom inverse converters with `custom_inverse_converters`, without using `register()`. By default, if the type is not registered and has a `__dict__` attribute, it's converted to the `frozendict` of that `__dict__`. This function assumes that hashable == immutable (that is not always true). This function uses recursion, with all the limits of recursions in Python. Where is a good old tail call when you need it? """ from frozendict import frozendict if custom_converters is None: custom_converters = frozendict() if custom_inverse_converters is None: custom_inverse_converters = frozendict() for type_i, converter in custom_converters.items(): if not issubclass(type(type_i), type): raise ValueError( f"{type_i} in `custom_converters` parameter is not a " + "type" ) try: converter.__call__ except AttributeError: raise ValueError( f"converter for {type_i} in `custom_converters` " + "parameter is not a callable" ) for type_i, converter in custom_inverse_converters.items(): if not issubclass(type(type_i), type): raise ValueError( f"{type_i} in `custom_inverse_converters` parameter " + "is not a type" ) try: converter.__call__ except AttributeError: raise ValueError( f"converter for {type_i} in " + "`custom_inverse_converters`parameter is not a callable" ) type_o = type(o) freeze_types = tuple(custom_converters.keys()) + getFreezeTypes() base_type_o = None for freeze_type in freeze_types: if isinstance(o, freeze_type): base_type_o = freeze_type break if base_type_o is None: # this is before hash check because all object in Python are # hashable by default, if not explicitly suppressed try: o.__dict__ except AttributeError: pass else: return frozendict(o.__dict__) try: hash(o) except TypeError: pass else: # without a converter, we can only hope that # hashable == immutable return o supported_types = ", ".join((x.__name__ for x in freeze_types)) err = ( f"type {type_o} is not hashable or is not equal or a " + f"subclass of the supported types: {supported_types}" ) raise TypeError(err) freeze_conversion_map = getFreezeConversionMap() freeze_conversion_map = freeze_conversion_map | custom_converters if base_type_o in _freeze_types_plain: return freeze_conversion_map[base_type_o](o) if not isIterableNotString(o): return freeze_conversion_map[base_type_o](o) freeze_conversion_inverse_map = getFreezeConversionInverseMap() freeze_conversion_inverse_map = ( freeze_conversion_inverse_map | custom_inverse_converters ) frozen_type = base_type_o in freeze_conversion_inverse_map if frozen_type: o = freeze_conversion_inverse_map[base_type_o](o) from copy import copy o_copy = copy(o) for k, v in getItems(o_copy)(o_copy): o_copy[k] = deepfreeze( v, custom_converters = custom_converters, custom_inverse_converters = custom_inverse_converters ) try: freeze = freeze_conversion_map[base_type_o] except KeyError: if frozen_type: freeze = type_o else: # pragma: no cover raise return freeze(o_copy) __all__ = ( deepfreeze.__name__, register.__name__, unregister.__name__, getFreezeConversionMap.__name__, getFreezeConversionInverseMap.__name__, FreezeError.__name__, FreezeWarning.__name__, ) del MappingProxyType del array del frozendict del MutableMapping del MutableSequence del MutableSet del Enum python-frozendict/src/frozendict/core.py000066400000000000000000000003321470250572200210060ustar00rootroot00000000000000# this provides compatibility for older pickles # created on a python-only implementation that # specifically mention frozendict.core.frozendict # noinspection PyUnresolvedReferences from frozendict import frozendict python-frozendict/src/frozendict/monkeypatch.py000066400000000000000000000136571470250572200224160ustar00rootroot00000000000000_OldJsonEncoder = None _oldOrjsonDumps = None _oldMutableMappingSubclasshook = None class MonkeypatchWarning(UserWarning): pass def checkCExtension(*, warn, warn_c = False): import frozendict as cool res = cool.c_ext if warn and res == warn_c: if warn_c: # pragma: no cover msg = "C Extension version, monkeypatch will be not applied" else: msg = "Pure Python version, monkeypatch will be not applied" import warnings warnings.warn(msg, MonkeypatchWarning) return res def patchOrUnpatchJson(*, patch, warn = True): # pragma: no cover if not checkCExtension(warn = warn): return from importlib import import_module self = import_module(__name__) import frozendict as cool import json OldJsonEncoder = self._OldJsonEncoder # noinspection PyUnresolvedReferences, PyProtectedMember FrozendictJsonEncoder = cool._getFrozendictJsonEncoder( OldJsonEncoder ) if patch: DefaultJsonEncoder = FrozendictJsonEncoder else: DefaultJsonEncoder = OldJsonEncoder if DefaultJsonEncoder is None: default_json_encoder = None else: default_json_encoder = DefaultJsonEncoder( skipkeys = False, ensure_ascii = True, check_circular = True, allow_nan = True, indent = None, separators = None, default = None, ) if patch: if OldJsonEncoder is None: self._OldJsonEncoder = json.encoder.JSONEncoder else: if OldJsonEncoder is None: raise ValueError( "Old json encoder is None " + "(maybe you already unpatched json?)" ) self._OldJsonEncoder = None cool.FrozendictJsonEncoder = FrozendictJsonEncoder json.JSONEncoder = DefaultJsonEncoder json.encoder.JSONEncoder = DefaultJsonEncoder json._default_encoder = default_json_encoder def patchOrUnpatchOrjson(*, patch, warn = True): # pragma: no cover if not checkCExtension(warn = warn): return from importlib import import_module self = import_module(__name__) # noinspection PyUnresolvedReferences import orjson if self._oldOrjsonDumps is None: if not patch: raise ValueError( "Old orjson encoder is None " + "(maybe you already unpatched orjson?)" ) oldOrjsonDumps = orjson.dumps else: oldOrjsonDumps = self._oldOrjsonDumps if patch: from frozendict import frozendict def frozendictOrjsonDumps(obj, *args, **kwargs): if isinstance(obj, frozendict): obj = dict(obj) return oldOrjsonDumps(obj, *args, **kwargs) defaultOrjsonDumps = frozendictOrjsonDumps newOldOrjsonDumps = oldOrjsonDumps else: defaultOrjsonDumps = oldOrjsonDumps newOldOrjsonDumps = None self._oldOrjsonDumps = newOldOrjsonDumps orjson.dumps = defaultOrjsonDumps orjson.orjson.dumps = defaultOrjsonDumps def patchOrUnpatchMutableMappingSubclasshook( *, patch, warn = True ): # pragma: no cover warn_c = True if checkCExtension(warn = warn, warn_c = warn_c): return from importlib import import_module self = import_module(__name__) from collections.abc import MutableMapping from frozendict import frozendict if self._oldMutableMappingSubclasshook is None: if not patch: raise ValueError( "Old MutableMapping subclasshook is None " + "(maybe you already unpatched MutableMapping?)" ) oldMutableMappingSubclasshook = MutableMapping.__subclasshook__ else: oldMutableMappingSubclasshook = self._oldMutableMappingSubclasshook if patch: # noinspection PyDecorator @classmethod def frozendictMutableMappingSubclasshook( klass, subclass, *args, **kwargs ): if klass == MutableMapping: if issubclass(subclass, frozendict): return False return oldMutableMappingSubclasshook( subclass, *args, **kwargs ) return NotImplemented defaultMutableMappingSubclasshook = frozendictMutableMappingSubclasshook newOldMutableMappingSubclasshook = oldMutableMappingSubclasshook else: defaultMutableMappingSubclasshook = oldMutableMappingSubclasshook newOldMutableMappingSubclasshook = None self._oldMutableMappingSubclasshook = newOldMutableMappingSubclasshook MutableMapping.__subclasshook__ = defaultMutableMappingSubclasshook try: # noinspection PyUnresolvedReferences, PyProtectedMember MutableMapping._abc_caches_clear() except AttributeError: # noinspection PyUnresolvedReferences, PyProtectedMember MutableMapping._abc_cache.discard(frozendict) # noinspection PyUnresolvedReferences, PyProtectedMember MutableMapping._abc_negative_cache.discard(frozendict) def patchOrUnpatchAll(*, patch, warn = True, raise_orjson = False): patchOrUnpatchJson(patch = patch, warn = warn) try: import orjson except ImportError: # pragma: no cover if raise_orjson: raise else: # pragma: no cover patchOrUnpatchOrjson(patch = patch, warn = warn) patchOrUnpatchMutableMappingSubclasshook(patch = patch, warn = warn) __all__ = ( patchOrUnpatchJson.__name__, patchOrUnpatchOrjson.__name__, patchOrUnpatchMutableMappingSubclasshook.__name__, patchOrUnpatchAll.__name__, MonkeypatchWarning.__name__, ) python-frozendict/src/frozendict/py.typed000066400000000000000000000000001470250572200211730ustar00rootroot00000000000000python-frozendict/src/frozendict/version.py000066400000000000000000000000221470250572200215370ustar00rootroot00000000000000version = "2.4.6" python-frozendict/test/000077500000000000000000000000001470250572200155275ustar00rootroot00000000000000python-frozendict/test/__init__.py000066400000000000000000000000001470250572200176260ustar00rootroot00000000000000python-frozendict/test/base.py000066400000000000000000000060301470250572200170120ustar00rootroot00000000000000import pytest class FrozendictTestBase: _FrozendictClass = None @property def FrozendictClass(self): val = self._FrozendictClass if val is None: raise ValueError("FrozendictClass is None") return val @FrozendictClass.setter def FrozendictClass(self, val): self._FrozendictClass = val _c_ext = None @property def c_ext(self): val = self._c_ext if val is None: raise ValueError("c_ext is None") return val @c_ext.setter def c_ext(self, val): self._c_ext = val _is_subclass = None @property def is_subclass(self): val = self._is_subclass if val is None: raise ValueError("is_subclass is None") return val @is_subclass.setter def is_subclass(self, val): self._is_subclass = val #################################################################### # dict fixtures @pytest.fixture def fd_dict(self): return { "Guzzanti": "Corrado", "Hicks": "Bill", self.FrozendictClass({1: 2}): "frozen" } @pytest.fixture def fd_dict_hole(self, fd_dict): new_dict = fd_dict.copy() del new_dict["Guzzanti"] return new_dict @pytest.fixture def fd_dict_eq(self): return { "Hicks": "Bill", "Guzzanti": "Corrado", self.FrozendictClass({1: 2}): "frozen" } @pytest.fixture def fd_dict_2(self): return { "Guzzanti": "Corrado", "Hicks": "Bill", "frozen": self.FrozendictClass({1: 2}) } @pytest.fixture def fd_dict_sabina(self): return {'Corrado': 'Guzzanti', 'Sabina': 'Guzzanti'} @pytest.fixture def generator_seq2(self, fd_dict): seq2 = list(fd_dict.items()) seq2.append(("Guzzanti", "Mario")) return (x for x in seq2) #################################################################### # frozendict fixtures @pytest.fixture def fd(self, fd_dict): return self.FrozendictClass(fd_dict) @pytest.fixture def fd_hole(self, fd_dict_hole): return self.FrozendictClass(fd_dict_hole) @pytest.fixture def fd_unhashable(self): return self.FrozendictClass({1: []}) @pytest.fixture def fd_eq(self, fd_dict_eq): return self.FrozendictClass(fd_dict_eq) @pytest.fixture def fd2(self, fd_dict_2): return self.FrozendictClass(fd_dict_2) @pytest.fixture def fd_sabina(self, fd_dict_sabina): return self.FrozendictClass(fd_dict_sabina) @pytest.fixture def fd_items(self, fd_dict): return tuple(fd_dict.items()) @pytest.fixture def fd_empty(self): return self.FrozendictClass() @pytest.fixture def module_prefix(self): if self.is_subclass: return "" return "frozendict." python-frozendict/test/bench.py000077500000000000000000000166671470250572200172030ustar00rootroot00000000000000#!/usr/bin/env python3 from frozendict import frozendict def main(number): import timeit import uuid from time import time from math import sqrt def mindev(data, xbar = None): """ This function calculates the stdev around the _minimum_ data, and not the mean """ if not data: raise ValueError("No data") if xbar is None: xbar = min(data) sigma2 = 0 for x in data: sigma2 += (x - xbar) ** 2 N = len(data) - 1 if N < 1: N = 1 return sqrt(sigma2 / N) def autorange( stmt, setup="pass", globals=None, ratio=1000, bench_time=10, number=None ): if setup is None: setup = "pass" t = timeit.Timer(stmt=stmt, setup=setup, globals=globals) break_immediately = False if number is None: # get automatically the number of needed loops a = t.autorange() num = a[0] # the number of loops number = int(num / ratio) if number < 1: number = 1 # the number of repeat of loops repeat = int(num / number) if repeat < 1: repeat = 1 else: repeat = 1 break_immediately = True data_tmp = t.repeat(number=number, repeat=repeat) min_value = min(data_tmp) # I create a list with minimum values data_min = [min_value] bench_start = time() while 1: # I get additional benchs until `bench_time` seconds passes data_min.extend(t.repeat(number=number, repeat=repeat)) if break_immediately or time() - bench_start > bench_time: break # I sort the data... data_min.sort() # ... so the minimum is the fist datum xbar = data_min[0] i = 0 # I repeat until no data is removed while i < len(data_min): i = len(data_min) # I calculate the sigma using the minimum as "real" value, # and not the mean sigma = mindev(data_min, xbar=xbar) for i2 in range(2, len(data_min)): # I thind the point where the data are greater than # 3 sigma. Data are sorted... if data_min[i2] - xbar > 3 * sigma: break k = i # do not remove too much data if i < 5: k = 5 # remove the data with sigma > 3 del data_min[k:] # I return the minimum as real value, with the sigma # calculated around the minimum return ( min(data_min) / number, mindev(data_min, xbar=xbar) / number ) def getUuid(): return str(uuid.uuid4()) dictionary_sizes = (5, 1000) print_tpl = ( "Name: {name: <25} Size: {size: >4}; Keys: {keys: >3}; " + "Type: {type: >10}; Time: {time:.2e}; Sigma: {sigma:.0e}" ) str_key = '12323f29-c31f-478c-9b15-e7acc5354df9' int_key = dictionary_sizes[0] - 2 if int_key < 0: int_key = 0 benchmarks = ( {"name": "constructor(d)", "code": "klass(d)", "setup": "klass = type(o)", }, {"name": "constructor(kwargs)", "code": "klass(**d)", "setup": "klass = type(o)", }, {"name": "constructor(seq2)", "code": "klass(v)", "setup": "klass = type(o); v = tuple(d.items())", }, {"name": "constructor(o)", "code": "klass(o)", "setup": "klass = type(o)", }, {"name": "o.copy()", "code": "o.copy()", "setup": "pass", }, {"name": "o == o", "code": "o == o", "setup": "pass", }, {"name": "for x in o", "code": "for _ in o: pass", "setup": "pass", }, {"name": "for x in o.values()", "code": "for _ in values: pass", "setup": "values = o.values()", }, {"name": "for x in o.items()", "code": "for _ in items: pass", "setup": "items = o.items()", }, {"name": "pickle.dumps(o)", "code": "dumps(o, protocol=-1)", "setup": "from pickle import dumps", }, {"name": "pickle.loads(dump)", "code": "loads(dump)", "setup": "from pickle import loads, dumps; dump = dumps(o, protocol=-1)", }, {"name": "class.fromkeys()", "code": "fromkeys(keys)", "setup": "fromkeys = type(o).fromkeys; keys = o.keys()", }, {"name": "set", "code": None, "setup": "val = getUuid()", }, ) dict_collection = [] for n in dictionary_sizes: d1 = {} d2 = {} for i in range(n-1): d1[getUuid()] = getUuid() d2[i] = i d1[str_key] = getUuid() d2[999] = 999 fd1 = frozendict(d1) fd2 = frozendict(d2) dict_collection.append({ "str": ((d1, fd1), str_key), "int": ((d2, fd2), int_key) }) for benchmark in benchmarks: print("#" * 72) for dict_entry in dict_collection: for (dict_keys, (dicts, one_key)) in dict_entry.items(): if ( benchmark["name"] == "constructor(kwargs)" and dict_keys == "int" ): continue print("/" * 72) for o in dicts: if ( benchmark["name"] == "hash(o)" and type(o) is dict ): continue if benchmark["name"] == "set": if type(o) is dict: benchmark["code"] = "o[one_key] = val" else: benchmark["code"] = "o.set(one_key, val)" d = dicts[0] bench_res = autorange( stmt = benchmark["code"], setup = benchmark["setup"], globals = { "o": o.copy(), "getUuid": getUuid, "d": d.copy(), "one_key": one_key }, number = number, ) print(print_tpl.format( name = "`{}`;".format(benchmark["name"]), keys = dict_keys, size = len(o), type = type(o).__name__, time = bench_res[0], sigma = bench_res[1], )) print("#" * 72) if __name__ == "__main__": import sys number = None argv = sys.argv len_argv = len(argv) max_positional_args = 1 max_len_argv = max_positional_args + 1 if len_argv > max_len_argv: raise ValueError( f"{__name__} must not accept more than " + f"{max_positional_args} positional command-line parameters" ) number_arg_pos = 1 if len_argv == number_arg_pos + 1: number = int(argv[number_arg_pos]) main(number) python-frozendict/test/common.py000066400000000000000000000351521470250572200173770ustar00rootroot00000000000000import pytest import pickle from copy import deepcopy import sys from collections.abc import MutableMapping from .base import FrozendictTestBase pyversion = sys.version_info pyversion_major = pyversion[0] pyversion_minor = pyversion[1] class Map(MutableMapping): def __init__(self, *args, **kwargs): self._dict = dict(*args, **kwargs) def __getitem__(self, key): return self._dict[key] def __setitem__(self, key, value): self._dict[key] = value def __delitem__(self, key): del self._dict[key] def __iter__(self): return iter(self._dict) def __len__(self): return len(self._dict) # noinspection PyMethodMayBeStatic class FrozendictCommonTest(FrozendictTestBase): @property def is_mapping_implemented(self): return self.c_ext or ( pyversion_major > 3 or (pyversion_major == 3 and pyversion_minor > 9) ) @property def is_reversed_implemented(self): return self.c_ext or ( pyversion_major > 3 or (pyversion_major == 3 and pyversion_minor > 7) ) #################################################################### # main tests def test_bool_false(self, fd_empty): assert not fd_empty def test_constructor_kwargs(self, fd2, fd_dict_2): assert self.FrozendictClass(**fd_dict_2) == fd2 def test_constructor_self(self, fd): assert fd == self.FrozendictClass(fd, Guzzanti="Corrado") def test_constructor_generator(self, fd, generator_seq2): assert fd == self.FrozendictClass( generator_seq2, Guzzanti="Corrado" ) def test_constructor_hole(self, fd_hole, fd_dict_hole): assert fd_hole == self.FrozendictClass(fd_dict_hole) def test_constructor_map(self, fd_dict): assert self.FrozendictClass(Map(fd_dict)) == fd_dict def test_normalget(self, fd): assert fd["Guzzanti"] == "Corrado" def test_keyerror(self, fd): with pytest.raises(KeyError): # noinspection PyStatementEffect fd["Brignano"] def test_len(self, fd, fd_dict): assert len(fd) == len(fd_dict) def test_in_true(self, fd): assert "Guzzanti" in fd def test_not_in_false(self, fd): assert not ("Guzzanti" not in fd) def test_in_false(self, fd): assert not ("Brignano" in fd) def test_not_in_true(self, fd): assert "Brignano" not in fd def test_bool_true(self, fd): assert fd def test_deepcopy_unhashable(self, fd_unhashable): fd_copy = deepcopy(fd_unhashable) assert fd_copy == fd_unhashable assert fd_copy is not fd_unhashable def test_not_equal(self, fd, fd2, fd_sabina): assert fd != fd_sabina assert fd != fd2 assert not (fd == fd_sabina) assert not (fd == fd2) def test_equals_dict(self, fd, fd_dict): assert fd == fd_dict @pytest.mark.parametrize( "protocol", range(pickle.HIGHEST_PROTOCOL + 1) ) def test_pickle(self, fd, protocol): dump = pickle.dumps(fd, protocol=protocol) assert dump assert pickle.loads(dump) == fd def test_constructor_iterator(self, fd, fd_items): assert self.FrozendictClass(fd_items) == fd def test_todict(self, fd, fd_dict): assert dict(fd) == fd_dict def test_get(self, fd): assert fd.get("Guzzanti") == "Corrado" def test_get_fail(self, fd): default = object() assert fd.get("Brignano", default) is default def test_keys(self, fd, fd_dict): assert tuple(fd.keys()) == tuple(fd_dict.keys()) def test_values(self, fd, fd_dict): assert tuple(fd.values()) == tuple(fd_dict.values()) def test_items(self, fd, fd_dict): assert tuple(fd.items()) == tuple(fd_dict.items()) def test_fromkeys(self, fd_sabina): keys = ["Corrado", "Sabina"] f = self.FrozendictClass.fromkeys(keys, "Guzzanti") assert f == fd_sabina def test_fromkeys_dict(self, fd_sabina, fd_dict_sabina): f = self.FrozendictClass.fromkeys(fd_dict_sabina, "Guzzanti") assert f == fd_sabina def test_fromkeys_set(self, fd_sabina, fd_dict_sabina): f = self.FrozendictClass.fromkeys( set(fd_dict_sabina), "Guzzanti" ) assert f == fd_sabina def test_repr(self, fd, fd_dict, module_prefix): classname = self.FrozendictClass.__name__ dict_repr = repr(fd_dict) assert repr(fd) == f"{module_prefix}{classname}({dict_repr})" def test_str(self, fd, fd_dict, module_prefix): classname = self.FrozendictClass.__name__ assert str(fd) == f"{module_prefix}{classname}({repr(fd_dict)})" def test_format(self, fd, fd_dict, module_prefix): classname = self.FrozendictClass.__name__ dict_repr = repr(fd_dict) assert format(fd) == f"{module_prefix}{classname}({dict_repr})" def test_iter(self, fd): items = [] for x in iter(fd): items.append((x, fd[x])) assert tuple(items) == tuple(fd.items()) def test_sum(self, fd, fd_dict, fd_dict_sabina): new_fd = fd | fd_dict_sabina assert type(new_fd) is self.FrozendictClass new_dict = dict(fd_dict) new_dict.update(fd_dict_sabina) assert new_fd == new_dict def test_union(self, fd_dict, fd_sabina): new_fd = self.FrozendictClass(fd_dict) id_fd = id(new_fd) new_fd |= fd_sabina assert type(new_fd) is self.FrozendictClass assert id_fd != id(new_fd) new_dict = dict(fd_dict) new_dict.update(fd_sabina) assert new_fd == new_dict def test_reversed(self, fd, fd_dict): assert (tuple(reversed(fd)) == tuple(reversed(tuple(fd_dict)))) def test_iter_len(self, fd): assert iter(fd).__length_hint__() >= 0 def test_keys_len(self, fd): assert len(fd.keys()) == len(fd) def test_items_len(self, fd): assert len(fd.items()) == len(fd) def test_values_len(self, fd): assert len(fd.values()) == len(fd) def test_equal_keys(self, fd, fd_dict): assert fd.keys() == fd_dict.keys() def test_equal_items(self, fd, fd_dict): assert fd.items() == fd_dict.items() def test_in_keys(self, fd): assert "Guzzanti" in fd.keys() def test_in_items(self, fd): assert ("Guzzanti", "Corrado") in fd.items() def test_disjoint_true_keys(self, fd, fd_sabina): assert fd.keys().isdisjoint(fd_sabina) def test_disjoint_true_items(self, fd, fd_sabina): assert fd.items().isdisjoint(fd_sabina.items()) def test_disjoint_false_keys(self, fd): assert not fd.keys().isdisjoint(fd) def test_disjoint_false_items(self, fd): assert not fd.items().isdisjoint(fd.items()) def test_sub_keys(self, fd, fd_dict, fd2, fd_dict_2): res = frozenset(fd_dict.keys()) - frozenset(fd_dict_2.keys()) assert fd.keys() - fd2.keys() == res def test_sub_items(self, fd, fd_dict, fd2, fd_dict_2): res = frozenset(fd_dict.items()) - frozenset(fd_dict_2.items()) assert fd.items() - fd2.items() == res def test_and_keys(self, fd, fd_dict, fd2, fd_dict_2): res = frozenset(fd_dict.keys()) & frozenset(fd_dict_2.keys()) assert fd.keys() & fd2.keys() == res def test_and_items(self, fd, fd_dict, fd2, fd_dict_2): res = frozenset(fd_dict.items()) & frozenset(fd_dict_2.items()) assert fd.items() & fd2.items() == res def test_or_keys(self, fd, fd_dict, fd2, fd_dict_2): res = frozenset(fd_dict.keys()) | frozenset(fd_dict_2.keys()) assert fd.keys() | fd2.keys() == res def test_or_items(self, fd, fd_dict, fd2, fd_dict_2): res = frozenset(fd_dict.items()) | frozenset(fd_dict_2.items()) assert fd.items() | fd2.items() == res def test_xor_keys(self, fd, fd_dict, fd2, fd_dict_2): res = frozenset(fd_dict.keys()) ^ frozenset(fd_dict_2.keys()) assert fd.keys() ^ fd2.keys() == res def test_xor_items(self, fd, fd_dict, fd2, fd_dict_2): res = frozenset(fd_dict.items()) ^ frozenset(fd_dict_2.items()) assert fd.items() ^ fd2.items() == res @pytest.mark.parametrize( "protocol", range(pickle.HIGHEST_PROTOCOL + 1) ) def test_pickle_iter_key(self, fd, protocol): orig = iter(fd.keys()) dump = pickle.dumps(orig, protocol=protocol) assert dump assert tuple(pickle.loads(dump)) == tuple(orig) @pytest.mark.parametrize( "protocol", range(pickle.HIGHEST_PROTOCOL + 1) ) def test_pickle_iter_item(self, fd, protocol): orig = iter(fd.items()) dump = pickle.dumps(orig, protocol=protocol) assert dump assert tuple(pickle.loads(dump)) == tuple(orig) @pytest.mark.parametrize( "protocol", range(pickle.HIGHEST_PROTOCOL + 1) ) def test_pickle_iter_value(self, fd, protocol): orig = iter(fd.values()) dump = pickle.dumps(orig, protocol=protocol) assert dump assert tuple(pickle.loads(dump)) == tuple(orig) def test_lt_key(self, fd, fd_hole): assert fd_hole.keys() < fd.keys() def test_gt_key(self, fd, fd_hole): assert fd.keys() > fd_hole.keys() def test_le_key(self, fd, fd_hole): assert fd_hole.keys() <= fd.keys() assert fd.keys() <= fd.keys() def test_ge_key(self, fd, fd_hole): assert fd.keys() >= fd_hole.keys() assert fd.keys() >= fd.keys() def test_lt_item(self, fd, fd_hole): assert fd_hole.items() < fd.items() def test_gt_item(self, fd, fd_hole): assert fd.items() > fd_hole.items() def test_le_item(self, fd, fd_hole): assert fd_hole.items() <= fd.items() assert fd.items() <= fd.items() def test_ge_item(self, fd, fd_hole): assert fd.items() >= fd_hole.items() assert fd.items() >= fd.items() def test_mapping_keys(self, fd): if not self.is_mapping_implemented: pytest.skip("mapping not implemented") assert fd.keys().mapping == fd def test_mapping_items(self, fd): if not self.is_mapping_implemented: pytest.skip("mapping not implemented") assert fd.items().mapping == fd def test_mapping_values(self, fd): if not self.is_mapping_implemented: pytest.skip("mapping not implemented") assert fd.values().mapping == fd def test_reversed_keys(self, fd, fd_dict): if not self.is_reversed_implemented: pytest.skip("reversed not implemented") fd_keys = tuple(reversed(fd.keys())) dict_keys = tuple(reversed(tuple(fd_dict.keys()))) assert fd_keys == dict_keys def test_reversed_items(self, fd, fd_dict): if not self.is_reversed_implemented: pytest.skip("reversed not implemented") fd_items = tuple(reversed(fd.items())) dict_items = tuple(reversed(tuple(fd_dict.items()))) assert fd_items == dict_items def test_reversed_values(self, fd, fd_dict): if not self.is_reversed_implemented: pytest.skip("reversed not implemented") fd_values = tuple(reversed(fd.values())) dict_values = tuple(reversed(tuple(fd_dict.values()))) assert fd_values == dict_values #################################################################### # frozendict-only tests def test_hash(self, fd, fd_eq): assert hash(fd) assert hash(fd) == hash(fd_eq) def test_unhashable_value(self, fd_unhashable): with pytest.raises(TypeError): hash(fd_unhashable) # hash is cached with pytest.raises(TypeError): hash(fd_unhashable) def test_set_replace(self, fd_dict, generator_seq2): items = tuple(generator_seq2) d2 = dict(items) assert fd_dict != d2 f2 = self.FrozendictClass(items) fd3 = f2.set("Guzzanti", "Corrado") assert fd3 == fd_dict def test_set_add(self, fd_dict): d2 = dict(fd_dict, a="b") assert fd_dict != d2 f2 = self.FrozendictClass(fd_dict) fd3 = f2.set("a", "b") assert fd3 == d2 def test_setdefault_notinsert(self, fd, fd_dict): assert fd.setdefault("Hicks") is fd def test_setdefault_insert_default(self, fd, fd_dict): fd_dict.setdefault("Allen") assert fd_dict == fd.setdefault("Allen") def test_setdefault_insert(self, fd, fd_dict): fd_dict.setdefault("Allen", "Woody") assert fd_dict == fd.setdefault("Allen", "Woody") def test_del(self, fd_dict): d2 = dict(fd_dict) d2["a"] = "b" f2 = self.FrozendictClass(d2) fd3 = f2.delete("a") assert fd3 == fd_dict def test_key(self, fd): assert fd.key() == fd.key(0) == "Guzzanti" assert fd.key(1) == fd.key(-2) == "Hicks" def test_key_out_of_range(self, fd): with pytest.raises(IndexError): fd.key(3) with pytest.raises(IndexError): fd.key(-4) def test_value(self, fd): assert fd.value() == fd.value(0) == "Corrado" assert fd.value(1) == fd.value(-2) == "Bill" def test_value_out_of_range(self, fd): with pytest.raises(IndexError): fd.value(3) with pytest.raises(IndexError): fd.value(-4) def test_item(self, fd): assert fd.item() == fd.item(0) == ("Guzzanti", "Corrado") assert fd.item(1) == fd.item(-2) == ("Hicks", "Bill") def test_item_out_of_range(self, fd): with pytest.raises(IndexError): fd.item(3) with pytest.raises(IndexError): fd.item(-4) #################################################################### # immutability tests def test_normalset(self, fd): with pytest.raises(TypeError): fd["Guzzanti"] = "Caterina" def test_del_error(self, fd): with pytest.raises(TypeError): del fd["Guzzanti"] def test_clear(self, fd): with pytest.raises(AttributeError): fd.clear() def test_pop(self, fd): with pytest.raises(AttributeError): fd.pop("Guzzanti") def test_popitem(self, fd): with pytest.raises(AttributeError): fd.popitem() def test_update(self, fd): with pytest.raises(AttributeError): fd.update({"Brignano": "Enrico"}) def test_delattr(self, fd): with pytest.raises(AttributeError): del fd.items python-frozendict/test/debug.py000077500000000000000000000343061470250572200172000ustar00rootroot00000000000000#!/usr/bin/env python3 import frozendict assert frozendict.c_ext from frozendict import frozendict from uuid import uuid4 import pickle from copy import copy, deepcopy from collections.abc import MutableMapping import tracemalloc import gc import sys def getUuid(): return str(uuid4()) class F(frozendict): def __new__(cls, *args, **kwargs): return super().__new__(cls, *args, **kwargs) class FMissing(frozendict): def __new__(cls, *args, **kwargs): return super().__new__(cls, *args, **kwargs) def __missing__(self, key): return key class Map(MutableMapping): def __init__(self, *args, **kwargs): self._dict = dict(*args, **kwargs) def __getitem__(self, key): return self._dict[key] def __setitem__(self, key, value): self._dict[key] = value def __delitem__(self, key): del self._dict[key] def __iter__(self): return iter(self._dict) def __len__(self): return len(self._dict) def print_info(klass, iterations, func): try: name = func.__name__ except AttributeError: name = func print( ( f"Class = {klass.__name__} - Loops: {iterations} - " + f"Evaluating: {name}" ), flush=True ) def print_sep(): print("-" * 72, flush=True) def trace(iterations = 100, mult = 10.0): def decorator(func): def wrapper(): header = ( f"Loops: {iterations} - Multiplier: {mult} " + f"Evaluating: {func.__name__}" ) print(header, flush=True) tracemalloc.start() snapshot1 = tracemalloc.take_snapshot().filter_traces( (tracemalloc.Filter(True, __file__), ) ) for i in range(iterations): func() gc.collect() snapshot2 = tracemalloc.take_snapshot().filter_traces( (tracemalloc.Filter(True, __file__), ) ) top_stats = snapshot2.compare_to(snapshot1, 'lineno') tracemalloc.stop() for stat in top_stats: if stat.count_diff * mult > iterations: header = ( f"Error. count diff: {stat.count_diff}, " + f"stat: {stat}" ) print(header, flush=True) sys.exit(1) return wrapper return decorator key_in = getUuid() dict_1 = {key_in: 0} dict_tmp = {getUuid(): i for i in range(1, 1000)} dict_1.update(dict_tmp) generator_1 = ((key, val) for key, val in dict_1.items()) key_notin = getUuid() dict_2 = {getUuid(): i for i in range(1000)} dict_3 = {i: i for i in range(1000)} dict_hole = dict_1.copy() del dict_hole[key_in] dict_unashable = dict_1.copy() dict_unashable.update({getUuid(): []}) dict_1_items = tuple(dict_1.items()) dict_1_keys = tuple(dict_1.keys()) dict_1_keys_set = set(dict_1_keys) functions = [] @trace(iterations = 300, mult = 1.5) def func_1(): pickle.loads(pickle.dumps(fd_1)) functions.append(func_1) @trace(iterations = 200, mult = 1.5) def func_2(): pickle.loads(pickle.dumps(iter(fd_1.keys()))) functions.append(func_2) @trace(iterations = 300, mult = 1.5) def func_3(): pickle.loads(pickle.dumps(iter(fd_1.items()))) functions.append(func_3) @trace(iterations = 400, mult = 1.5) def func_4(): pickle.loads(pickle.dumps(iter(fd_1.values()))) functions.append(func_4) @trace() def func_5(): frozendict_class({}) functions.append(func_5) @trace() def func_7(): frozendict_class([]) functions.append(func_7) @trace() def func_8(): frozendict_class({}, **{}) functions.append(func_8) @trace() def func_9(): frozendict_class(**dict_1) functions.append(func_9) @trace() def func_10(): frozendict_class(dict_1, **dict_2) functions.append(func_10) @trace() def func_11(): frozendict_class(fd_1) functions.append(func_11) @trace() def func_12(): frozendict_class(generator_1) functions.append(func_12) @trace() def func_13(): frozendict_class(dict_hole) functions.append(func_13) @trace() def func_14(): fd_1.copy() functions.append(func_14) @trace() def func_15(): # noinspection PyStatementEffect fd_1 == dict_1 functions.append(func_15) @trace() def func_16(): # noinspection PyStatementEffect fd_1 == fd_1 functions.append(func_16) @trace() def func_17(): # noinspection PyStatementEffect fd_1 != dict_hole functions.append(func_17) @trace() def func_18(): # noinspection PyStatementEffect fd_1 != dict_2 functions.append(func_18) @trace() def func_19(): # noinspection PyStatementEffect fd_1 == dict_hole functions.append(func_19) @trace() def func_20(): # noinspection PyStatementEffect fd_1 == dict_2 functions.append(func_20) @trace() def func_21(): frozendict_class(dict_1) functions.append(func_21) @trace() def func_22(): frozendict_class(dict_1_items) functions.append(func_22) @trace() def func_23(): tuple(fd_1.keys()) functions.append(func_23) @trace() def func_24(): tuple(fd_1.values()) functions.append(func_24) @trace() def func_25(): tuple(fd_1.items()) functions.append(func_25) @trace() def func_26(): frozendict_class.fromkeys(dict_1) functions.append(func_26) @trace() def func_27(): frozendict_class.fromkeys(dict_1, 1) functions.append(func_27) @trace() def func_28(): frozendict_class.fromkeys(dict_1_keys) functions.append(func_28) @trace() def func_29(): frozendict_class.fromkeys(dict_1_keys, 1) functions.append(func_29) @trace() def func_30(): frozendict_class.fromkeys(dict_1_keys_set) functions.append(func_30) @trace() def func_31(): frozendict_class.fromkeys(dict_1_keys_set, 1) functions.append(func_31) @trace() def func_32(): repr(fd_1) functions.append(func_32) @trace() def func_33(): fd_1 | dict_2 functions.append(func_33) @trace() def func_34(): hash(fd_1) functions.append(func_34) @trace() def func_35(): # noinspection PyStatementEffect frozendict_class() == frozendict_class() functions.append(func_35) @trace() def func_36(): tuple(reversed(fd_1)) functions.append(func_36) @trace() def func_37(): tuple(reversed(fd_1.keys())) functions.append(func_37) @trace() def func_38(): tuple(reversed(fd_1.items())) functions.append(func_38) @trace() def func_39(): tuple(reversed(fd_1.values())) functions.append(func_39) @trace() def func_40(): iter(fd_1).__length_hint__() functions.append(func_40) @trace() def func_41(): len(fd_1) functions.append(func_41) @trace() def func_42(): len(fd_1.keys()) functions.append(func_42) @trace() def func_43(): len(fd_1.items()) functions.append(func_43) @trace() def func_44(): len(fd_1.values()) functions.append(func_44) @trace() def func_45(): # noinspection PyStatementEffect,PyUnresolvedReferences fd_1.keys().mapping == fd_1 functions.append(func_45) @trace() def func_46(): # noinspection PyStatementEffect, PyUnresolvedReferences fd_1.items().mapping == fd_1 functions.append(func_46) @trace() def func_47(): # noinspection PyStatementEffect, PyUnresolvedReferences fd_1.values().mapping == fd_1 functions.append(func_47) @trace() def func_48(): # noinspection PyStatementEffect fd_1[key_in] functions.append(func_48) @trace() def func_49(): fd_1.get(key_in) functions.append(func_49) @trace() def func_50(): fd_1.get(key_notin) functions.append(func_50) @trace() def func_51(): fd_1.get(key_notin, 1) functions.append(func_51) @trace() def func_52(): # noinspection PyStatementEffect key_in in fd_1 functions.append(func_52) @trace() def func_53(): # noinspection PyStatementEffect key_notin in fd_1 functions.append(func_53) @trace() def func_54(): fd_1.copy() functions.append(func_54) @trace() def func_55(): copy(fd_1) functions.append(func_55) @trace() def func_56(): deepcopy(fd_1) functions.append(func_56) @trace() def func_57(): deepcopy(fd_unashable) functions.append(func_57) @trace() def func_58(): # noinspection PyStatementEffect fd_1.keys() == dict_1.keys() functions.append(func_58) @trace() def func_59(): # noinspection PyStatementEffect fd_1.items() == dict_1.items() functions.append(func_59) @trace() def func_60(): # noinspection PyStatementEffect key_notin in fd_1.keys() functions.append(func_60) @trace() def func_61(): # noinspection PyStatementEffect (key_notin, 0) in fd_1.items() functions.append(func_61) @trace() def func_62(): # noinspection PyStatementEffect FMissing(fd_1)[0] functions.append(func_62) @trace() def func_63(): mp = Map(dict_1) # noinspection PyStatementEffect frozendict_class(mp) == dict_1 functions.append(func_63) @trace() def func_64(): fd_1.keys().isdisjoint(dict_3) functions.append(func_64) @trace() def func_65(): fd_1.keys().isdisjoint(fd_1) functions.append(func_65) @trace() def func_66(): fd_1.items().isdisjoint(dict_3.items()) functions.append(func_66) @trace() def func_67(): fd_1.items().isdisjoint(fd_1.items()) functions.append(func_67) @trace() def func_68(): fd_unashable.keys() - fd_1.keys() functions.append(func_68) @trace() def func_69(): fd_1.items() - frozendict_class(dict_hole).items() functions.append(func_69) @trace() def func_70(): fd_1.keys() & frozendict_class(dict_hole).keys() functions.append(func_70) @trace() def func_71(): fd_1.items() & frozendict_class(dict_hole).items() functions.append(func_71) @trace() def func_72(): fd_1.keys() | frozendict_class(dict_2).keys() functions.append(func_72) @trace() def func_73(): fd_1.items() | frozendict_class(dict_2).items() functions.append(func_73) @trace() def func_74(): fd_1.keys() ^ frozendict_class(dict_hole).keys() functions.append(func_74) @trace() def func_75(): fd_1.items() ^ frozendict_class(dict_hole).items() functions.append(func_75) @trace() def func_76(): frozendict_class(dict_unashable) functions.append(func_76) @trace() def func_77(): frozendict_class(dict_3) functions.append(func_77) @trace() def func_78(): frozendict_class() functions.append(func_78) @trace() def func_79(): # noinspection PyStatementEffect frozendict_class(dict_hole).keys() < fd_1.keys() functions.append(func_79) @trace() def func_80(): # noinspection PyStatementEffect frozendict_class(dict_hole).keys() <= fd_1.keys() functions.append(func_80) @trace() def func_81(): # noinspection PyStatementEffect frozendict_class(dict_hole).items() < fd_1.items() functions.append(func_81) @trace() def func_82(): # noinspection PyStatementEffect fd_1.keys() > frozendict_class(dict_hole).keys() functions.append(func_82) @trace() def func_83(): # noinspection PyStatementEffect fd_1.keys() >= frozendict_class(dict_hole).keys() functions.append(func_83) @trace() def func_84(): # noinspection PyStatementEffect fd_1.items() > frozendict_class(dict_hole).items() functions.append(func_84) @trace() def func_85(): # noinspection PyStatementEffect fd_1.items() >= frozendict_class(dict_hole).items() functions.append(func_85) @trace() def func_86(): fd_1.set(key_in, 1000) functions.append(func_86) @trace() def func_87(): fd_1.set(key_notin, 1000) functions.append(func_87) @trace() def func_88(): fd_1.delete(key_in) functions.append(func_88) @trace() def func_89(): fd_1.setdefault(key_in) functions.append(func_89) @trace() def func_90(): fd_1.setdefault(key_notin) functions.append(func_90) @trace() def func_91(): fd_1.setdefault(key_notin, 1000) functions.append(func_91) @trace() def func_92(): fd_1.key() functions.append(func_92) @trace() def func_93(): fd_1.key(0) functions.append(func_93) @trace() def func_94(): fd_1.key(-1) functions.append(func_94) @trace() def func_95(): fd_1.value() functions.append(func_95) @trace() def func_96(): fd_1.value(0) functions.append(func_96) @trace() def func_97(): fd_1.value(-1) functions.append(func_97) @trace() def func_98(): fd_1.item() functions.append(func_98) @trace() def func_99(): fd_1.item(0) functions.append(func_99) @trace() def func_100(): fd_1.item(-1) functions.append(func_100) @trace() def func_101(): for _ in fd_1: pass functions.append(func_101) @trace() def func_102(): for _ in iter(fd_1): pass functions.append(func_102) @trace() def func_103(): for _ in fd_1.keys(): pass functions.append(func_103) @trace() def func_104(): for _ in fd_1.values(): pass functions.append(func_104) @trace() def func_105(): for _ in fd_1.items(): pass functions.append(func_105) @trace() def func_106(): try: hash(fd_unashable) except TypeError: pass functions.append(func_106) @trace() def func_107(): try: fd_1[key_notin] except KeyError: pass functions.append(func_107) @trace() def func_108(): try: fd_1.key(len(fd_1)) except IndexError: pass functions.append(func_108) @trace() def func_109(): try: fd_1.value(len(fd_1)) except IndexError: pass functions.append(func_109) @trace() def func_110(): try: fd_1.item(len(fd_1)) except IndexError: pass functions.append(func_110) print_sep() for frozendict_class in (frozendict, F): print_info( frozendict_class, 1, "frozendict_class(dict_1)" ) fd_1 = frozendict_class(dict_1) print_sep() print_info( frozendict_class, 1, "frozendict_class(dict_unashable)" ) fd_unashable = frozendict_class(dict_unashable) print_sep() for function in functions: print( f"Class = {frozendict_class.__name__} - ", flush = True, end = "" ) function() print_sep() python-frozendict/test/frozendict_only.py000066400000000000000000000035131470250572200213130ustar00rootroot00000000000000import io import pickle import pytest from copy import copy, deepcopy from .base import FrozendictTestBase # noinspection PyMethodMayBeStatic class FrozendictOnlyTest(FrozendictTestBase): def test_empty(self, fd_empty): fd_empty_set = self.FrozendictClass({}) fd_empty_list = self.FrozendictClass([]) fd_empty_dict = self.FrozendictClass({}, **{}) assert fd_empty is fd_empty_set is fd_empty_list assert fd_empty is fd_empty_dict def test_constructor_self_1(self, fd): assert fd is self.FrozendictClass(fd) def test_vars(self, fd): with pytest.raises(TypeError): vars(fd) def test_setattr(self, fd): with pytest.raises(AttributeError): fd._initialized = False def test_copy(self, fd): # noinspection PyTestUnpassedFixture assert fd.copy() is fd def test_copycopy(self, fd, fd_unhashable): assert copy(fd) is fd assert copy(fd_unhashable) is fd_unhashable def test_deepcopy(self, fd): assert deepcopy(fd) is fd def test_init(self, fd): # noinspection PyTestUnpassedFixture fd_copy = fd.copy() fd_clone = self.FrozendictClass(dict(fd)) fd.__init__({"Trump": "Donald"}) assert fd_copy is fd assert fd_clone == fd def test_del_empty(self): f = self.FrozendictClass({1: 2}) assert f.delete(1) is self.FrozendictClass() def test_pickle_core(self, fd): class CustomUnpickler(pickle.Unpickler): def find_class(self, module, name): assert module == 'frozendict' assert name == 'frozendict' return super().find_class('frozendict.core', name) dump = pickle.dumps(fd) assert dump assert CustomUnpickler(io.BytesIO(dump)).load() == fd python-frozendict/test/subclass_only.py000066400000000000000000000044451470250572200207700ustar00rootroot00000000000000from copy import copy, deepcopy from .base import FrozendictTestBase # noinspection PyMethodMayBeStatic class FrozendictSubclassOnlyTest(FrozendictTestBase): _FrozendictMissingClass = None @property def FrozendictMissingClass(self): val = self._FrozendictMissingClass if val is None: raise ValueError("FrozendictMissingClass is None") return val @FrozendictMissingClass.setter def FrozendictMissingClass(self, val): self._FrozendictMissingClass = val #################################################################### # tests def test_empty_sub(self, fd_empty): fd_empty_2 = self.FrozendictClass({}) fd_empty_3 = self.FrozendictClass([]) fd_empty_4 = self.FrozendictClass({}, **{}) assert fd_empty == fd_empty_2 == fd_empty_3 == fd_empty_4 assert fd_empty is not fd_empty_2 is not fd_empty_3 assert fd_empty is not fd_empty_4 def test_constructor_self_sub(self, fd): fd_clone = self.FrozendictClass(fd) assert fd == fd_clone assert fd is not fd_clone def test_copy_sub(self, fd): # noinspection PyTestUnpassedFixture fd_copy = fd.copy() assert fd_copy == fd assert fd_copy is not fd def test_copycopy_sub(self, fd, fd_unhashable): fd_copy = copy(fd) fd_copy_unhashable = copy(fd_unhashable) assert fd_copy == fd assert fd_copy_unhashable == fd_unhashable assert fd_copy is not fd assert fd_copy_unhashable is not fd_unhashable def test_deepcopy_sub(self, fd): fd_copy = deepcopy(fd) assert fd_copy == fd assert fd_copy is not fd def test_init_sub(self, fd): # noinspection PyTestUnpassedFixture fd_copy = fd.copy() fd_clone = self.FrozendictClass(dict(fd)) fd.__init__({"Trump": "Donald"}) assert fd_copy == fd assert fd_clone == fd assert fd_copy is not fd def test_del_empty_sub(self, fd_empty): f = self.FrozendictClass({1: 2}) f2 = f.delete(1) assert f2 == fd_empty assert f2 is not fd_empty def test_missing(self, fd): fd_missing = self.FrozendictMissingClass(fd) assert fd_missing[0] == 0 python-frozendict/test/test_freeze.py000066400000000000000000000123251470250572200204230ustar00rootroot00000000000000import pytest import frozendict as cool from frozendict import frozendict from collections import OrderedDict from collections.abc import MutableSequence, Sequence, Iterable from array import array from types import MappingProxyType from frozendict import FreezeError, FreezeWarning from enum import Enum class A: def __init__(self, x): self.x = x class NoDictAndHash: __slots__ = ( "x", ) def __init__(self, x): self.x = x def __hash__(self): raise TypeError() class BaseSeq(Sequence): def __init__(self, seq): self._items = list(seq) def __getitem__(self, i): return self._items[i] def __len__(self): return len(self._items) class FrozenSeqA(BaseSeq): pass class FrozenSeqB(BaseSeq): pass class MutableSeq(BaseSeq, MutableSequence): def __delitem__(self, i): del self._items[i] def __setitem__(self, i, v): self._items[i] = v def insert(self, index, value): self._items.insert(index, value) def custom_a_converter(a): return frozendict(**a.__dict__, y="mbuti") def custom_inverse_converter(o): return dict(**o, y="mbuti") @pytest.fixture def before_cure_inverse(): return [frozendict(a={1: 2})] @pytest.fixture def after_cure_inverse(): return (frozendict(a=frozendict({1: 2}), y="mbuti"), ) @pytest.fixture def no_cure_inverse(): return (frozendict(a=frozendict({1: 2})), ) @pytest.fixture def a(): a = A(3) return a @pytest.fixture def no_dict_and_hash(): res = NoDictAndHash(3) return res @pytest.fixture def before_cure(a): return {"x": [ 5, frozendict(y = {5, "b", memoryview(b"b")}), array("B", (0, 1, 2)), OrderedDict(a=bytearray(b"a")), MappingProxyType({2: []}), a ]} @pytest.fixture def after_cure(): return frozendict(x = ( 5, frozendict(y = frozenset({5, "b", memoryview(b"b")})), (0, 1, 2), frozendict(a = b'a'), MappingProxyType({2: ()}), frozendict(x = 3), )) @pytest.fixture def after_cure_a(a): return custom_a_converter(a) @pytest.fixture def my_enum(): Color = Enum('Color', ['RED', 'GREEN', 'BLUE']) # noinspection PyUnresolvedReferences return Color.RED def test_deepfreeze(before_cure, after_cure): assert cool.deepfreeze(before_cure) == after_cure def test_register_bad_to_convert(): with pytest.raises(ValueError): cool.register(5, 7) def test_register_bad_converter(): with pytest.raises(ValueError): cool.register(frozendict, 7) def test_unregister_not_present(): with pytest.raises(FreezeError): cool.unregister(frozendict) def test_deepfreeze_bad_custom_converters_key(before_cure): with pytest.raises(ValueError): cool.deepfreeze(before_cure, custom_converters={7: 7}) def test_deepfreeze_bad_custom_converters_val(before_cure): with pytest.raises(ValueError): cool.deepfreeze(before_cure, custom_converters={frozendict: 7}) def test_deepfreeze_bad_custom_inverse_converters_key(before_cure): with pytest.raises(ValueError): cool.deepfreeze(before_cure, custom_inverse_converters={7: 7}) def test_deepfreeze_bad_custom_inverse_converters_val(before_cure): with pytest.raises(ValueError): cool.deepfreeze( before_cure, custom_inverse_converters = {frozendict: 7} ) def test_register_custom(a): cool.register(A, custom_a_converter) assert cool.deepfreeze(a) == custom_a_converter(a) cool.unregister(A) assert cool.deepfreeze(a) == frozendict(a.__dict__) def test_deepfreeze_custom(a): assert cool.deepfreeze( a, custom_converters={A: custom_a_converter} ) == custom_a_converter(a) def test_deepfreeze_inverse(before_cure_inverse, after_cure_inverse): assert cool.deepfreeze( before_cure_inverse, custom_inverse_converters={frozendict: custom_inverse_converter} ) == after_cure_inverse def test_register_inverse( before_cure_inverse, after_cure_inverse, no_cure_inverse, ): with pytest.warns(FreezeWarning): cool.register( frozendict, custom_inverse_converter, inverse=True ) assert cool.deepfreeze(before_cure_inverse) == after_cure_inverse cool.unregister(frozendict, inverse=True) assert cool.deepfreeze(before_cure_inverse) == no_cure_inverse def test_prefer_forward(): assert isinstance( cool.deepfreeze( [FrozenSeqA((0, 1, 2))], custom_converters={FrozenSeqA: FrozenSeqB}, custom_inverse_converters={FrozenSeqA: MutableSeq} )[0], FrozenSeqB ) def test_original_immutate(): unfrozen = { "int": 1, "nested": {"int": 1}, } cool.deepfreeze(unfrozen) assert type(unfrozen["nested"]) is dict def test_enum(my_enum): assert cool.deepfreeze(my_enum) is my_enum def test_get_items(): with pytest.raises(TypeError): cool.cool.getItems(5) def test_no_dict_and_hash(no_dict_and_hash): with pytest.raises(TypeError): cool.deepfreeze(no_dict_and_hash) python-frozendict/test/test_frozendict.py000066400000000000000000000005331470250572200213100ustar00rootroot00000000000000from .common import FrozendictCommonTest from .frozendict_only import FrozendictOnlyTest import frozendict as cool from frozendict import frozendict as FrozendictClass is_subclass = False class TestFrozendict(FrozendictCommonTest, FrozendictOnlyTest): FrozendictClass = FrozendictClass c_ext = cool.c_ext is_subclass = is_subclass python-frozendict/test/test_frozendict_subclass.py000066400000000000000000000014161470250572200232100ustar00rootroot00000000000000from .common import FrozendictCommonTest from .subclass_only import FrozendictSubclassOnlyTest import frozendict as cool from frozendict import frozendict as FrozendictClass is_subclass = True class FrozendictSubclass(FrozendictClass): def __new__(cls, *args, **kwargs): return super().__new__(cls, *args, **kwargs) class FrozendictMissingSubclass(FrozendictClass): def __new__(cls, *args, **kwargs): return super().__new__(cls, *args, **kwargs) def __missing__(self, key): return key class TestFrozendictSubclass( FrozendictCommonTest, FrozendictSubclassOnlyTest ): FrozendictClass = FrozendictSubclass FrozendictMissingClass = FrozendictMissingSubclass c_ext = cool.c_ext is_subclass = is_subclass python-frozendict/test/test_monkeypatch.py000066400000000000000000000017351470250572200214700ustar00rootroot00000000000000import json import frozendict as cool import pytest from frozendict import frozendict from frozendict.monkeypatch import MonkeypatchWarning class A: pass @pytest.fixture def object_to_serialize(): return frozendict() @pytest.fixture def object_to_serialize_2(): return A() @pytest.fixture def serialized_object(): return "{}" def test_get_json_encoder( object_to_serialize, object_to_serialize_2, serialized_object, ): if cool.c_ext: cool.monkeypatch.patchOrUnpatchJson(patch=True) else: with pytest.warns(MonkeypatchWarning): cool.monkeypatch.patchOrUnpatchJson(patch=True, warn=True) assert json.dumps(object_to_serialize) == serialized_object with pytest.raises(TypeError): json.dumps(object_to_serialize_2) cool.monkeypatch.patchOrUnpatchJson(patch=False, warn=False) if cool.c_ext: with pytest.raises(TypeError): json.dumps(object_to_serialize) python-frozendict/test/typed.py000077500000000000000000000035401470250572200172330ustar00rootroot00000000000000#!/usr/bin/env python3 import copy from collections.abc import Hashable from typing import ItemsView, Iterator, KeysView, Mapping, ValuesView from frozendict import frozendict from typing_extensions import assert_type fd = frozendict(a=1, b=2) assert_type(frozendict(), frozendict[()]) assert_type(fd, frozendict[str, int]) assert_type(fd["a"], int) assert_type(iter(fd), Iterator[str]) assert_type(frozendict.fromkeys("abc", 0), frozendict[str, int]) assert_type(fd.copy(), frozendict[str, int]) assert_type(fd.item(0), tuple[str, int]) assert_type(fd.item(), tuple[str, int]) assert_type(fd.key(0), str) assert_type(fd.key(), str) assert_type(fd.value(0), int) assert_type(fd.value(), int) assert_type(fd.get("a"), int | None) assert_type(fd.items(), ItemsView[str, int]) assert_type(fd.keys(), KeysView[str]) assert_type(fd.values(), ValuesView[int]) assert_type(copy.copy(fd), frozendict[str, int]) assert_type(copy.deepcopy(fd), frozendict[str, int]) assert_type(reversed(fd), reversed) assert_type(fd.delete("a"), frozendict[str, int]) assert_type(fd | {"c": 2}, frozendict[str, int]) assert_type(fd | {1: 2}, frozendict[str | int, int]) assert_type(fd | {"c": "c"}, frozendict[str, str | int]) assert_type(fd | {1: "c"}, frozendict[str | int, str | int]) assert_type(fd.setdefault("a", 0), frozendict[str, int]) assert_type(fd.setdefault("a", "a"), frozendict[str, str | int]) assert_type(fd.setdefault(0, 0), frozendict[str | int, int]) assert_type(fd.setdefault(0, "a"), frozendict[str | int, str | int]) assert_type(fd.set("abc", 1), frozendict[str, int]) assert_type(fd.set("abc", "abc"), frozendict[str, str | int]) assert_type(fd.set(1, 1), frozendict[str | int, int]) assert_type(fd.set(1, "abc"), frozendict[str | int, str | int]) # frozendict implements Mapping[K, V] and is covariant in V vals: frozendict[str, Hashable] = fd mapping: Mapping[str, Hashable] = fd python-frozendict/todo/000077500000000000000000000000001470250572200155155ustar00rootroot00000000000000python-frozendict/todo/future.txt000066400000000000000000000010331470250572200175650ustar00rootroot00000000000000union intersection difference symmetric_difference(other) set ^ other Return a new set with elements in either the set or other but not both issubset(other) set <= other Test whether every element in the set is in other. set < other Test whether the set is a proper subset of other, that is, set <= other and set != other. issuperset(other) set >= other Test whether every element in other is in the set. set > other Test whether the set is a proper superset of other, that is, set >= other and set != other. python-frozendict/todo/todo.txt000066400000000000000000000003331470250572200172220ustar00rootroot00000000000000pydict_global_version -> _pydict_global_version calculate_keysize -> calculate_log2_keysize estimate_keysize -> estimate_log2_keysize _Py_dict_lookup -> _d_Py_dict_lookup insertion with grow? restore lookdict_nodummy?