Compare commits

...

11 Commits

Author SHA1 Message Date
Thomas Haller
86752d9dc8 Merge branch 'th/gdataset-index' into 'main'
[th/gdataset-index] add a lookup index (GHashTable) to `GData`

See merge request GNOME/glib!3885
2024-07-15 21:20:05 +00:00
Philip Withnall
d3cb9d638d Merge branch 'traverse-diagrams' into 'main'
docs(GNode): Traversal diagrams, color & dark-mode

See merge request GNOME/glib!4157
2024-07-15 15:58:45 +00:00
Philip Withnall
a931a75c4f docs(GNode): Traversal diagrams, color & dark-mode
* Create a dark-mode variant of each traversal diagram, with the
  traversal path colorized `--primary` blue, instead of the original
  black.
* Apply the same colorizations to the light-mode diagrams, but
  using the light-theme `--primary` blue.
* Add SPDX license/copyright comments to all eight SVG files.
* Add new files to documentation configs in `glib.toml.in`.
* Update documentation comment in `gnode.c` to embed both color
  variants via picture tags, instead of markdown image embeds.
* Add alt text to all four images.
* Add additional blank lines to documentation comment, so that
  a. First item in bulleted list does not get folded into
     preceding intro paragraph
  b. Intro paragraph and diagrams are not part of first paragraph
     in documentation. (This also gets them out of the top-level
     table-of-contents/index list, where they previously appeared
     in full.)
* (Accidental change I didn't realize I was making): Convert line
  endings in breadth-first diagram from CRLF to LF.
2024-07-15 15:58:45 +00:00
Thomas Haller
76e71478a8 gdataset: use lookup index in g_datalist_id_remove_multiple()
In order to reuse datalist_find(), we turn the loops around. First loop
over the keys, and then (internally in datalist_find()) loop over the
entries.
2024-05-30 08:18:36 +02:00
Thomas Haller
d7b3e4267a gdataset: handle up to G_MAXUINT32 entries
GData is a thread safe container. For that reason, it probably is anyway
not suitable to add 4 billion entries. That is even with the new index,
where we now scale with O(1).

Still, the code should not just break down at half of what we
theoretically could add.

Handle overflow and huge sizes correctly.
2024-05-30 08:18:36 +02:00
Thomas Haller
fff7ef4728 gdataset: use hash lookup in g_datalist_get_data()
g_datalist_get_data() tries to avoid g_quark_try_string() and instead
do the lock-less g_quark_to_string(). For small number of entries that
may perform better. At least, this was an optimization from the past.

However, if we have a GHashTable index, and presumably a larger number
of entries, we should use the index instead. Unfortunately, that requires
us again to use g_quark_try_string() and take a global lock.
2024-05-30 08:18:36 +02:00
Thomas Haller
2a45dc29a0 gdataset: add GHashTable lookup index to GData
GData tracks the entries in a linear array, and performs a linear
search. That is very good, as long as the number of entries is small.
However, when it grows, performance gets worse. It's not clear what the
exact threshold is, or what the recommended maximum is.

Also, GData is used to attach arbitrary user data to GObject. Which is a
great feature, that a user might want to use. In that case, they may be
less concerned about performance to get the benefits of the feature, and
thus add more data than recommended.

Also, GObject adds entries in its qdata. Hence, also for basic GObject
operations, it's important that this performs well. Note that access to
GData happens while holding a lock, we want to minimize the time while
holding that lock.

This patch ensures that access to GData is O(1) (and reasonably fast).
It also allows to use GData in ways that wasn't possible before.

There are alternatives, like using a binary search tree or always use a GHashTable.
Instead, we keep the linear buffer. Only when the buffer grows to
ALLOC_THRESHOLD_INDEX entries, we will start generating and maintaining
a GHashTable index. So the common case where objects have few entries
does not change. The memory overhead is only there, when the list grows.

ALLOC_THRESHOLD_INDEX is set to 64 entries. We will allocate such a
large buffer when adding the 33 entry. During shrink, we will drop the
buffer again when shrinking down to 16 entries.

The reason for not always using the GHashTable is to save the memory in
common cases where there are few entries.

We use g_hash_table_add() to exploit the GHashTable optimization. We
also let it point to the GDataElt from the linear array. The benefit is
that we don't require individual allocations. The downside is that
during reallocation we need to regenerate the entire index.

In my tests this actually performs well. For example, following are
timings for calling g_dataset_id_get_data() in a loop. This is the
lookup time for an entry that doesn't exist. For example, if you would
look up the first entry in the list, it's gonna be faster with linear
search.

   num-entries     time-before   time-after
             1           0.144        0.145
             2           0.146        0.146
             5           0.149        0.150
            10           0.155        0.157
            20           0.172        0.170
            32           0.248        0.254
            33           0.249        0.184  <== index in use
            40           0.284        0.183
            50           0.317        0.189
            75           0.370        0.184
           100           0.442        0.183
           300           1.044        0.186
          1000           3.170        0.184
         10000          31.597        0.189
2024-05-30 08:18:36 +02:00
Thomas Haller
c21e684001 gdataset: add datalist_realloc() helper
This will be more useful next.

Also, try to detect whether realloc() actually moved the pointer. If it
doesn't, we can optimize a bit (not use g_datalist_unlock_and_set()).
Next, we will do more useful optimization in this case.

Note that we cannot just compare dangling pointers. That's would be
undefined behavior. In practice, we probably often compare dangling
pointers, and this tends to work just fine. Still avoid this and compare
only the guintptr values.
2024-05-30 08:18:36 +02:00
Thomas Haller
7a1f795198 gdataset: extract helper function for struct size 2024-05-30 08:18:36 +02:00
Thomas Haller
b5de7dad8a gdataset: extract datalist_destory() helper function for releasing GData
Avoid duplicated code.

Also, the "if (data->data[i].data && " check is unnecessary.  The data
pointer is never NULL, because g_datalist_id_set_data() will treat that
as indication to remove the entry.
2024-05-30 08:18:36 +02:00
Thomas Haller
847f195808 gdataset/tests: improve "/datalist/id-remove-multiple/resize" test 2024-05-30 08:18:36 +02:00
12 changed files with 2992 additions and 291 deletions

View File

@ -0,0 +1,138 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-License-Identifier: CC0-1.0
SPDX-FileCopyrightText: Rory O'Kane at Wikimedia Commons
<https://commons.wikimedia.org/wiki/File:Sorted_binary_tree_breadth-first_traversal.svg>
SPDX-FileCopyrightText: 2024 Frank Dana
-->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="266px" height="212px" viewBox="0 0 266 212" enable-background="new 0 0 266 212" xml:space="preserve">
<g>
<g>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" x1="27.23" y1="23.192" x2="29.23" y2="23.192"/>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9562,3.9562" x1="33.187" y1="23.192" x2="236.932" y2="23.192"/>
<polyline fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" points="238.91,23.192 240.91,23.192
238.97,23.677 "/>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="24.646" x2="27.971" y2="76.427"/>
<polyline fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" points="26.035,76.912 24.095,77.396
26.095,77.396 "/>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0154,4.0154" x1="30.11" y1="77.396" x2="236.902" y2="77.396"/>
<polyline fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" points="238.91,77.396 240.91,77.396
238.97,77.881 "/>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="78.85" x2="27.971" y2="130.631"/>
<polyline fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" points="26.035,131.114 24.095,131.6
26.095,131.6 "/>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0154,4.0154" x1="30.11" y1="131.6" x2="236.902" y2="131.6"/>
<polyline fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" points="238.91,131.6 240.91,131.6
238.97,132.085 "/>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="133.053" x2="27.971" y2="184.835"/>
<polyline fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" points="26.035,185.318 24.095,185.804
26.095,185.804 "/>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0086,4.0086" x1="30.104" y1="185.804" x2="228.53" y2="185.804"/>
<line fill="none" stroke="#90c2ff" stroke-width="2" stroke-miterlimit="10" x1="230.534" y1="185.804" x2="232.534" y2="185.804"/>
<g>
<rect fill="#90c2ff" x="24.095" y="19.892" width="6.602" height="6.602"/>
</g>
<g>
<path fill="#90c2ff" d="M238.569,185.804c-2.84,1.054-6.363,2.852-8.548,4.756l1.721-4.756l-1.721-4.755
C232.206,182.953,235.729,184.751,238.569,185.804z"/>
</g>
</g>
</g>
<g id="graph0">
<title>sorted_binary_tree</title>
<g id="node1">
<title>C</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="60.23" cy="185.804" rx="18.067" ry="18.067"/>
<text transform="matrix(1 0 0 1 55.5435 190.8223)" font-family="'Times-Roman'" font-size="14.0528">C</text>
</g>
<g id="node2">
<title>E</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="132.502" cy="185.804" rx="18.067" ry="18.067"/>
<text transform="matrix(1 0 0 1 128.21 190.8223)" font-family="'Times-Roman'" font-size="14.0528">E</text>
</g>
<g id="node3">
<title>H</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="204.773" cy="185.804" rx="17.064" ry="18.067"/>
<text transform="matrix(1 0 0 1 199.6992 190.8223)" font-family="'Times-Roman'" font-size="14.0528">H</text>
</g>
<g id="node4">
<title>A</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="24.094" cy="131.6" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 19.02 136.6191)" font-family="'Times-Roman'" font-size="14.0528">A</text>
</g>
<g id="node5">
<title>D</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="96.366" cy="131.6" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 91.292 136.6191)" font-family="'Times-Roman'" font-size="14.0528">D</text>
</g>
<g id="edge6">
<title>D-&gt;C</title>
<path fill="none" stroke="#ffffff" d="M86.328,147.66c-3.011,4.016-7.026,9.034-10.038,14.053"/>
<polygon fill="#ffffff" stroke="#ffffff" points="78.298,164.725 70.268,170.747 73.279,160.709 "/>
</g>
<g id="edge8">
<title>D-&gt;E</title>
<path fill="none" stroke="#ffffff" d="M106.404,147.66c3.011,4.016,7.026,9.034,10.038,14.053"/>
<polygon fill="#ffffff" stroke="#ffffff" points="119.453,160.709 122.464,170.747 114.434,164.725 "/>
</g>
<g id="node6">
<title>I</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="240.909" cy="131.6" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 238.5693 136.6191)" font-family="'Times-Roman'" font-size="14.0528">I</text>
</g>
<g id="edge12">
<title>I-&gt;H</title>
<path fill="none" stroke="#ffffff" d="M230.871,146.656c-3.011,5.02-6.021,10.038-10.037,15.057"/>
<polygon fill="#ffffff" stroke="#ffffff" points="223.846,163.721 214.812,169.743 217.822,159.705 "/>
</g>
<g id="node7">
<title>B</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="60.23" cy="77.396" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 55.5435 82.415)" font-family="'Times-Roman'" font-size="14.0528">B</text>
</g>
<g id="edge3">
<title>B-&gt;A</title>
<path fill="none" stroke="#ffffff" d="M50.192,92.453c-3.011,5.019-6.022,10.038-10.038,15.057"/>
<polygon fill="#ffffff" stroke="#ffffff" points="43.166,109.518 34.132,115.539 37.144,105.502 "/>
</g>
<g id="edge5">
<title>B-&gt;D</title>
<path fill="none" stroke="#ffffff" d="M70.268,92.453c3.011,5.019,6.022,10.038,10.038,15.057"/>
<polygon fill="#ffffff" stroke="#ffffff" points="83.317,105.502 86.328,115.539 77.294,109.518 "/>
</g>
<g id="node8">
<title>G</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="204.773" cy="77.396" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 199.6992 82.415)" font-family="'Times-Roman'" font-size="14.0528">G</text>
</g>
<g id="edge11">
<title>G-&gt;I</title>
<path fill="none" stroke="#ffffff" d="M214.812,93.457c3.011,4.015,7.026,9.034,10.038,14.053"/>
<polygon fill="#ffffff" stroke="#ffffff" points="227.86,106.506 230.871,116.543 222.842,110.521 "/>
</g>
<g id="node9">
<title>F</title>
<ellipse fill="#FFFFFF" stroke="#ffffff" cx="132.502" cy="23.192" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 128.5942 28.2109)" font-family="'Times-Roman'" font-size="14.0528">F</text>
</g>
<g id="edge2">
<title>F-&gt;B</title>
<path fill="none" stroke="#ffffff" d="M117.445,34.234c-11.042,8.03-23.087,17.064-34.128,26.098"/>
<polygon fill="#ffffff" stroke="#ffffff" points="85.325,63.343 75.287,66.354 81.31,57.321 "/>
</g>
<g id="edge10">
<title>F-&gt;G</title>
<path fill="none" stroke="#ffffff" d="M147.559,34.234c11.041,8.03,23.087,17.064,34.129,26.098"/>
<polygon fill="#ffffff" stroke="#ffffff" points="183.694,57.321 189.717,66.354 179.68,63.343 "/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -1,134 +1,138 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="266px" height="212px" viewBox="0 0 266 212" enable-background="new 0 0 266 212" xml:space="preserve">
<g>
<g>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" x1="27.23" y1="23.192" x2="29.23" y2="23.192"/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9562,3.9562" x1="33.187" y1="23.192" x2="236.932" y2="23.192"/>
<polyline fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" points="238.91,23.192 240.91,23.192
238.97,23.677 "/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="24.646" x2="27.971" y2="76.427"/>
<polyline fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" points="26.035,76.912 24.095,77.396
26.095,77.396 "/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0154,4.0154" x1="30.11" y1="77.396" x2="236.902" y2="77.396"/>
<polyline fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" points="238.91,77.396 240.91,77.396
238.97,77.881 "/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="78.85" x2="27.971" y2="130.631"/>
<polyline fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" points="26.035,131.114 24.095,131.6
26.095,131.6 "/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0154,4.0154" x1="30.11" y1="131.6" x2="236.902" y2="131.6"/>
<polyline fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" points="238.91,131.6 240.91,131.6
238.97,132.085 "/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="133.053" x2="27.971" y2="184.835"/>
<polyline fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" points="26.035,185.318 24.095,185.804
26.095,185.804 "/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0086,4.0086" x1="30.104" y1="185.804" x2="228.53" y2="185.804"/>
<line fill="none" stroke="#000000" stroke-width="2" stroke-miterlimit="10" x1="230.534" y1="185.804" x2="232.534" y2="185.804"/>
<g>
<rect x="24.095" y="19.892" width="6.602" height="6.602"/>
</g>
<g>
<path d="M238.569,185.804c-2.84,1.054-6.363,2.852-8.548,4.756l1.721-4.756l-1.721-4.755
C232.206,182.953,235.729,184.751,238.569,185.804z"/>
</g>
</g>
</g>
<g id="graph0">
<title>sorted_binary_tree</title>
<g id="node1">
<title>C</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="60.23" cy="185.804" rx="18.067" ry="18.067"/>
<text transform="matrix(1 0 0 1 55.5435 190.8223)" font-family="'Times-Roman'" font-size="14.0528">C</text>
</g>
<g id="node2">
<title>E</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="132.502" cy="185.804" rx="18.067" ry="18.067"/>
<text transform="matrix(1 0 0 1 128.21 190.8223)" font-family="'Times-Roman'" font-size="14.0528">E</text>
</g>
<g id="node3">
<title>H</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="204.773" cy="185.804" rx="17.064" ry="18.067"/>
<text transform="matrix(1 0 0 1 199.6992 190.8223)" font-family="'Times-Roman'" font-size="14.0528">H</text>
</g>
<g id="node4">
<title>A</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="24.094" cy="131.6" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 19.02 136.6191)" font-family="'Times-Roman'" font-size="14.0528">A</text>
</g>
<g id="node5">
<title>D</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="96.366" cy="131.6" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 91.292 136.6191)" font-family="'Times-Roman'" font-size="14.0528">D</text>
</g>
<g id="edge6">
<title>D-&gt;C</title>
<path fill="none" stroke="#000000" d="M86.328,147.66c-3.011,4.016-7.026,9.034-10.038,14.053"/>
<polygon stroke="#000000" points="78.298,164.725 70.268,170.747 73.279,160.709 "/>
</g>
<g id="edge8">
<title>D-&gt;E</title>
<path fill="none" stroke="#000000" d="M106.404,147.66c3.011,4.016,7.026,9.034,10.038,14.053"/>
<polygon stroke="#000000" points="119.453,160.709 122.464,170.747 114.434,164.725 "/>
</g>
<g id="node6">
<title>I</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="240.909" cy="131.6" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 238.5693 136.6191)" font-family="'Times-Roman'" font-size="14.0528">I</text>
</g>
<g id="edge12">
<title>I-&gt;H</title>
<path fill="none" stroke="#000000" d="M230.871,146.656c-3.011,5.02-6.021,10.038-10.037,15.057"/>
<polygon stroke="#000000" points="223.846,163.721 214.812,169.743 217.822,159.705 "/>
</g>
<g id="node7">
<title>B</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="60.23" cy="77.396" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 55.5435 82.415)" font-family="'Times-Roman'" font-size="14.0528">B</text>
</g>
<g id="edge3">
<title>B-&gt;A</title>
<path fill="none" stroke="#000000" d="M50.192,92.453c-3.011,5.019-6.022,10.038-10.038,15.057"/>
<polygon stroke="#000000" points="43.166,109.518 34.132,115.539 37.144,105.502 "/>
</g>
<g id="edge5">
<title>B-&gt;D</title>
<path fill="none" stroke="#000000" d="M70.268,92.453c3.011,5.019,6.022,10.038,10.038,15.057"/>
<polygon stroke="#000000" points="83.317,105.502 86.328,115.539 77.294,109.518 "/>
</g>
<g id="node8">
<title>G</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="204.773" cy="77.396" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 199.6992 82.415)" font-family="'Times-Roman'" font-size="14.0528">G</text>
</g>
<g id="edge11">
<title>G-&gt;I</title>
<path fill="none" stroke="#000000" d="M214.812,93.457c3.011,4.015,7.026,9.034,10.038,14.053"/>
<polygon stroke="#000000" points="227.86,106.506 230.871,116.543 222.842,110.521 "/>
</g>
<g id="node9">
<title>F</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="132.502" cy="23.192" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 128.5942 28.2109)" font-family="'Times-Roman'" font-size="14.0528">F</text>
</g>
<g id="edge2">
<title>F-&gt;B</title>
<path fill="none" stroke="#000000" d="M117.445,34.234c-11.042,8.03-23.087,17.064-34.128,26.098"/>
<polygon stroke="#000000" points="85.325,63.343 75.287,66.354 81.31,57.321 "/>
</g>
<g id="edge10">
<title>F-&gt;G</title>
<path fill="none" stroke="#000000" d="M147.559,34.234c11.041,8.03,23.087,17.064,34.129,26.098"/>
<polygon stroke="#000000" points="183.694,57.321 189.717,66.354 179.68,63.343 "/>
</g>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!--
SPDX-License-Identifier: CC0-1.0
SPDX-FileCopyrightText: Rory O'Kane at Wikimedia Commons
<https://commons.wikimedia.org/wiki/File:Sorted_binary_tree_breadth-first_traversal.svg>
SPDX-FileCopyrightText: 2024 Frank Dana
-->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="266px" height="212px" viewBox="0 0 266 212" enable-background="new 0 0 266 212" xml:space="preserve">
<g>
<g>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" x1="27.23" y1="23.192" x2="29.23" y2="23.192"/>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9562,3.9562" x1="33.187" y1="23.192" x2="236.932" y2="23.192"/>
<polyline fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" points="238.91,23.192 240.91,23.192
238.97,23.677 "/>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="24.646" x2="27.971" y2="76.427"/>
<polyline fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" points="26.035,76.912 24.095,77.396
26.095,77.396 "/>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0154,4.0154" x1="30.11" y1="77.396" x2="236.902" y2="77.396"/>
<polyline fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" points="238.91,77.396 240.91,77.396
238.97,77.881 "/>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="78.85" x2="27.971" y2="130.631"/>
<polyline fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" points="26.035,131.114 24.095,131.6
26.095,131.6 "/>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0154,4.0154" x1="30.11" y1="131.6" x2="236.902" y2="131.6"/>
<polyline fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" points="238.91,131.6 240.91,131.6
238.97,132.085 "/>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="3.9907,3.9907" x1="235.099" y1="133.053" x2="27.971" y2="184.835"/>
<polyline fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" points="26.035,185.318 24.095,185.804
26.095,185.804 "/>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="4.0086,4.0086" x1="30.104" y1="185.804" x2="228.53" y2="185.804"/>
<line fill="none" stroke="#1c76e4" stroke-width="2" stroke-miterlimit="10" x1="230.534" y1="185.804" x2="232.534" y2="185.804"/>
<g>
<rect fill="#1c76e4" x="24.095" y="19.892" width="6.602" height="6.602"/>
</g>
<g>
<path fill="#1c76e4" d="M238.569,185.804c-2.84,1.054-6.363,2.852-8.548,4.756l1.721-4.756l-1.721-4.755
C232.206,182.953,235.729,184.751,238.569,185.804z"/>
</g>
</g>
</g>
<g id="graph0">
<title>sorted_binary_tree</title>
<g id="node1">
<title>C</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="60.23" cy="185.804" rx="18.067" ry="18.067"/>
<text transform="matrix(1 0 0 1 55.5435 190.8223)" font-family="'Times-Roman'" font-size="14.0528">C</text>
</g>
<g id="node2">
<title>E</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="132.502" cy="185.804" rx="18.067" ry="18.067"/>
<text transform="matrix(1 0 0 1 128.21 190.8223)" font-family="'Times-Roman'" font-size="14.0528">E</text>
</g>
<g id="node3">
<title>H</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="204.773" cy="185.804" rx="17.064" ry="18.067"/>
<text transform="matrix(1 0 0 1 199.6992 190.8223)" font-family="'Times-Roman'" font-size="14.0528">H</text>
</g>
<g id="node4">
<title>A</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="24.094" cy="131.6" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 19.02 136.6191)" font-family="'Times-Roman'" font-size="14.0528">A</text>
</g>
<g id="node5">
<title>D</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="96.366" cy="131.6" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 91.292 136.6191)" font-family="'Times-Roman'" font-size="14.0528">D</text>
</g>
<g id="edge6">
<title>D-&gt;C</title>
<path fill="none" stroke="#000000" d="M86.328,147.66c-3.011,4.016-7.026,9.034-10.038,14.053"/>
<polygon stroke="#000000" points="78.298,164.725 70.268,170.747 73.279,160.709 "/>
</g>
<g id="edge8">
<title>D-&gt;E</title>
<path fill="none" stroke="#000000" d="M106.404,147.66c3.011,4.016,7.026,9.034,10.038,14.053"/>
<polygon stroke="#000000" points="119.453,160.709 122.464,170.747 114.434,164.725 "/>
</g>
<g id="node6">
<title>I</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="240.909" cy="131.6" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 238.5693 136.6191)" font-family="'Times-Roman'" font-size="14.0528">I</text>
</g>
<g id="edge12">
<title>I-&gt;H</title>
<path fill="none" stroke="#000000" d="M230.871,146.656c-3.011,5.02-6.021,10.038-10.037,15.057"/>
<polygon stroke="#000000" points="223.846,163.721 214.812,169.743 217.822,159.705 "/>
</g>
<g id="node7">
<title>B</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="60.23" cy="77.396" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 55.5435 82.415)" font-family="'Times-Roman'" font-size="14.0528">B</text>
</g>
<g id="edge3">
<title>B-&gt;A</title>
<path fill="none" stroke="#000000" d="M50.192,92.453c-3.011,5.019-6.022,10.038-10.038,15.057"/>
<polygon stroke="#000000" points="43.166,109.518 34.132,115.539 37.144,105.502 "/>
</g>
<g id="edge5">
<title>B-&gt;D</title>
<path fill="none" stroke="#000000" d="M70.268,92.453c3.011,5.019,6.022,10.038,10.038,15.057"/>
<polygon stroke="#000000" points="83.317,105.502 86.328,115.539 77.294,109.518 "/>
</g>
<g id="node8">
<title>G</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="204.773" cy="77.396" rx="17.064" ry="18.068"/>
<text transform="matrix(1 0 0 1 199.6992 82.415)" font-family="'Times-Roman'" font-size="14.0528">G</text>
</g>
<g id="edge11">
<title>G-&gt;I</title>
<path fill="none" stroke="#000000" d="M214.812,93.457c3.011,4.015,7.026,9.034,10.038,14.053"/>
<polygon stroke="#000000" points="227.86,106.506 230.871,116.543 222.842,110.521 "/>
</g>
<g id="node9">
<title>F</title>
<ellipse fill="#FFFFFF" stroke="#000000" cx="132.502" cy="23.192" rx="18.068" ry="18.068"/>
<text transform="matrix(1 0 0 1 128.5942 28.2109)" font-family="'Times-Roman'" font-size="14.0528">F</text>
</g>
<g id="edge2">
<title>F-&gt;B</title>
<path fill="none" stroke="#000000" d="M117.445,34.234c-11.042,8.03-23.087,17.064-34.128,26.098"/>
<polygon stroke="#000000" points="85.325,63.343 75.287,66.354 81.31,57.321 "/>
</g>
<g id="edge10">
<title>F-&gt;G</title>
<path fill="none" stroke="#000000" d="M147.559,34.234c11.041,8.03,23.087,17.064,34.129,26.098"/>
<polygon stroke="#000000" points="183.694,57.321 189.717,66.354 179.68,63.343 "/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 36 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -99,6 +99,10 @@ content_images = [
"Sorted_binary_tree_inorder.svg",
"Sorted_binary_tree_postorder.svg",
"Sorted_binary_tree_preorder.svg",
"Sorted_binary_tree_breadth-first_traversal-dark.svg",
"Sorted_binary_tree_inorder-dark.svg",
"Sorted_binary_tree_postorder-dark.svg",
"Sorted_binary_tree_preorder-dark.svg",
]
[[object]]

View File

@ -68,6 +68,22 @@
#define G_DATALIST_FLAGS_MASK_INTERNAL 0x7
/* When GData.alloc grows to size ALLOC_THRESHOLD_INDEX, we reserve one additional
* GHashTable* at &data->data[data->alloc]. This will contain the index for fast
* lookup. See datalist_index*() helpers.
*
* Note that we grow the GData.data buffer by doubling the allocation size. So
* we first allocate 64 entries, when adding the 33 entry.
*
* Conversely, we exponentially shrink the buffer. That means, when we remove
* entries and reach 16 (or lower), we will shrink the buffer from 64 to 32
* entries (and stop using the index).
*
* So we start using the index when adding >= 33 entries. And stop using it
* when removing to <= 16 entries.
*/
#define ALLOC_THRESHOLD_INDEX 64u
#define G_DATALIST_CLEAN_POINTER(ptr) \
((GData *) ((gpointer) (((guintptr) (ptr)) & ~((guintptr) G_DATALIST_FLAGS_MASK_INTERNAL))))
@ -158,53 +174,178 @@ g_datalist_unlock_and_set (GData **datalist, gpointer ptr)
g_pointer_bit_unlock_and_set ((void **) datalist, DATALIST_LOCK_BIT, ptr, G_DATALIST_FLAGS_MASK_INTERNAL);
}
static gsize
datalist_alloc_size (guint32 alloc)
{
/* GDataElt also contains pointer. It thus is suitable aligned for pointers,
* and we can just append the pointer for the index at the end. */
return G_STRUCT_OFFSET (GData, data) +
(((gsize) alloc) * sizeof (GDataElt)) +
(G_UNLIKELY (alloc >= ALLOC_THRESHOLD_INDEX) ? sizeof (GHashTable *) : 0u);
}
G_ALWAYS_INLINE static inline GHashTable **
datalist_index_get_ptr (GData *data)
{
if (G_LIKELY (data->alloc < ALLOC_THRESHOLD_INDEX))
return NULL;
return (gpointer) (&(data->data[data->alloc]));
}
G_ALWAYS_INLINE static inline GHashTable *
datalist_index_get (GData *data)
{
GHashTable **p_index;
p_index = datalist_index_get_ptr (data);
#if G_ENABLE_DEBUG
g_assert (!p_index || *p_index);
#endif
return G_UNLIKELY (p_index) ? *p_index : NULL;
}
static guint
_datalist_index_hash (gconstpointer key)
{
const GQuark *ptr = key;
G_STATIC_ASSERT (G_STRUCT_OFFSET (GDataElt, key) == 0);
return *ptr;
}
static gboolean
_datalist_index_equal (gconstpointer a, gconstpointer b)
{
const GQuark *ptr_a = a;
const GQuark *ptr_b = b;
return *ptr_a == *ptr_b;
}
G_ALWAYS_INLINE static inline GHashTable *
datalist_index_new (void)
{
return g_hash_table_new (_datalist_index_hash, _datalist_index_equal);
}
static GData *
datalist_realloc (GData *data, guint32 alloc, gboolean *out_reallocated)
{
guintptr data_old;
gboolean reallocated;
GHashTable *index;
GHashTable **p_index;
guint32 i;
data_old = (guintptr) ((gpointer) data);
index = datalist_index_get (data);
data = g_realloc (data, datalist_alloc_size (alloc));
reallocated = (((guintptr) ((gpointer) (data))) != data_old);
data->alloc = alloc;
if (out_reallocated)
*out_reallocated = reallocated;
/* Note that if data was @reallocated, then @index contains only dangling pointers.
* We can only destroy/remove-all, which we rely on not following those pointers. */
p_index = datalist_index_get_ptr (data);
if (G_LIKELY (!p_index))
{
if (G_UNLIKELY (index))
g_hash_table_unref (index);
}
else if (!reallocated && index)
{
/* The index is still fine and the pointers are all still valid. We
* can keep it. */
*p_index = index;
}
else
{
if (G_UNLIKELY (index))
{
/* Note that the GHashTable's keys are now all dangling pointers!
* We rely on remove-all to not following them. */
g_hash_table_remove_all (index);
}
else
index = datalist_index_new ();
*p_index = index;
for (i = 0; i < data->len; i++)
g_hash_table_add (index, &data->data[i]);
}
return data;
}
static gboolean
datalist_append (GData **data, GQuark key_id, gpointer new_data, GDestroyNotify destroy_func)
{
GDataElt *data_elt;
GHashTable *index;
gboolean reallocated;
GData *d;
d = *data;
if (!d)
{
d = g_malloc (G_STRUCT_OFFSET (GData, data) + 2u * sizeof (GDataElt));
d = g_malloc (datalist_alloc_size (2u));
d->len = 0;
d->alloc = 2u;
if (2u >= ALLOC_THRESHOLD_INDEX)
*(datalist_index_get_ptr (d)) = datalist_index_new ();
*data = d;
reallocated = TRUE;
}
else if (d->len == d->alloc)
{
d->alloc = d->alloc * 2u;
#if G_ENABLE_DEBUG
/* d->alloc is always a power of two. It thus overflows the first time
* when going to (G_MAXUINT32+1), or when requesting 2^31+1 elements.
*
* This is not handled, and we just crash. That's because we track the GData
* in a linear list, which horribly degrades long before we add 2 billion entries.
* Don't ever try to do that. */
g_assert (d->alloc > d->len);
#endif
d = g_realloc (d, G_STRUCT_OFFSET (GData, data) + d->alloc * sizeof (GDataElt));
guint32 alloc = d->alloc * 2u;
if (G_UNLIKELY (alloc < d->alloc))
{
if (d->alloc == G_MAXUINT32)
g_error ("GData cannot contain more than 4294967295 entries");
alloc = G_MAXUINT32;
}
d = datalist_realloc (d, alloc, &reallocated);
*data = d;
reallocated = TRUE;
}
else
reallocated = FALSE;
d->data[d->len] = (GDataElt){
data_elt = &d->data[d->len];
*data_elt = (GDataElt){
.key = key_id,
.data = new_data,
.destroy = destroy_func,
};
d->len++;
index = datalist_index_get (d);
if (G_UNLIKELY (index))
g_hash_table_add (index, data_elt);
return reallocated;
}
static void
datalist_remove (GData *data, guint32 idx)
{
GHashTable *index;
#if G_ENABLE_DEBUG
g_assert (idx < data->len);
#endif
@ -214,15 +355,24 @@ datalist_remove (GData *data, guint32 idx)
* to @idx are left unchanged, and the last entry is moved to position @idx.
* */
index = datalist_index_get (data);
if (G_UNLIKELY (index))
g_hash_table_remove (index, &data->data[idx]);
data->len--;
if (idx != data->len)
data->data[idx] = data->data[data->len];
{
data->data[idx] = data->data[data->len];
if (G_UNLIKELY (index))
g_hash_table_add (index, &data->data[idx]);
}
}
static gboolean
datalist_shrink (GData **data, GData **d_to_free)
{
gboolean reallocated;
guint32 alloc_by_4;
guint32 v;
GData *d;
@ -239,10 +389,17 @@ datalist_shrink (GData **data, GData **d_to_free)
if (d->len == 0)
{
GHashTable *index;
/* The list became empty. We drop the allocated memory altogether. */
/* The caller will free the buffer after releasing the lock, to minimize
* the time we hold the lock. Transfer it out. */
index = datalist_index_get (d);
if (G_UNLIKELY (index))
g_hash_table_unref (index);
*d_to_free = d;
*data = NULL;
return TRUE;
@ -253,8 +410,9 @@ datalist_shrink (GData **data, GData **d_to_free)
v = d->len;
if (v != alloc_by_4)
{
/* d->alloc is a power of two. Usually, we remove one element at a
* time, then we will just reach reach a quarter of that.
/* d->alloc is a power of two (unless it's G_MAXUINT32). Usually, we
* remove one element at a time, then we will just reach reach a quarter
* of that.
*
* However, with g_datalist_id_remove_multiple(), len can be smaller
* at once. In that case, find first the next power of two. */
@ -264,27 +422,55 @@ datalist_shrink (GData **data, GData **d_to_free)
#if G_ENABLE_DEBUG
g_assert (v > d->len);
g_assert (v <= d->alloc / 2u);
g_assert (v <= (d->alloc == G_MAXUINT32 ? 0x80000000u : d->alloc / 2u));
#endif
d->alloc = v;
d = g_realloc (d, G_STRUCT_OFFSET (GData, data) + (v * sizeof (GDataElt)));
d = datalist_realloc (d, v, &reallocated);
*d_to_free = NULL;
*data = d;
return TRUE;
return reallocated;
}
static void
datalist_destroy (GData *data)
{
GHashTable *index;
guint32 i;
/* Must be called without lock. Will free @data and invoke the
* destroy() notifications. */
index = datalist_index_get (data);
if (G_UNLIKELY (index))
g_hash_table_unref (index);
for (i = 0; i < data->len; i++)
{
if (data->data[i].destroy)
data->data[i].destroy (data->data[i].data);
}
g_free (data);
}
static GDataElt *
datalist_find (GData *data, GQuark key_id, guint32 *out_idx)
{
GDataElt *data_elt;
GHashTable *index;
guint32 i;
if (data)
if (!data)
goto out_not_found;
index = datalist_index_get (data);
if (G_LIKELY (!index))
{
/* We have no index. Do a linear search. */
for (i = 0; i < data->len; i++)
{
GDataElt *data_elt = &data->data[i];
data_elt = &data->data[i];
if (data_elt->key == key_id)
{
if (out_idx)
@ -292,7 +478,23 @@ datalist_find (GData *data, GQuark key_id, guint32 *out_idx)
return data_elt;
}
}
goto out_not_found;
}
data_elt = g_hash_table_lookup (index, &key_id);
if (!data_elt)
goto out_not_found;
#if G_ENABLE_DEBUG
g_assert (data_elt >= data->data && data_elt < &data->data[data->len]);
#endif
if (out_idx)
*out_idx = (data_elt - data->data);
return data_elt;
out_not_found:
if (out_idx)
*out_idx = G_MAXUINT32;
return NULL;
@ -310,7 +512,6 @@ void
g_datalist_clear (GData **datalist)
{
GData *data;
guint i;
g_return_if_fail (datalist != NULL);
@ -324,13 +525,7 @@ g_datalist_clear (GData **datalist)
g_datalist_unlock_and_set (datalist, NULL);
for (i = 0; i < data->len; i++)
{
if (data->data[i].data && data->data[i].destroy)
data->data[i].destroy (data->data[i].data);
}
g_free (data);
datalist_destroy (data);
}
/* HOLDS: g_dataset_global_lock */
@ -359,7 +554,6 @@ g_dataset_destroy_internal (GDataset *dataset)
while (dataset)
{
GData *data;
guint i;
data = G_DATALIST_GET_POINTER (&dataset->datalist);
@ -376,12 +570,7 @@ g_dataset_destroy_internal (GDataset *dataset)
G_UNLOCK (g_dataset_global);
for (i = 0; i < data->len; i++)
{
if (data->data[i].data && data->data[i].destroy)
data->data[i].destroy (data->data[i].data);
}
g_free (data);
datalist_destroy (data);
G_LOCK (g_dataset_global);
dataset = g_dataset_lookup (dataset_location);
@ -530,7 +719,9 @@ g_data_remove_internal (GData **datalist,
GData *d_to_free;
gsize found_keys;
gsize i_keys;
guint32 i_data;
if (n_keys == 0)
return;
d = g_datalist_lock_and_get (datalist);
@ -556,33 +747,23 @@ g_data_remove_internal (GData **datalist,
old = old_to_free;
}
i_data = 0;
found_keys = 0;
while (i_data < d->len && found_keys < n_keys)
for (i_keys = 0; i_keys < n_keys; i_keys++)
{
GDataElt *data = &d->data[i_data];
gboolean remove = FALSE;
GDataElt *data_elt;
guint32 idx;
for (i_keys = 0; i_keys < n_keys; i_keys++)
{
if (data->key == keys[i_keys])
{
/* We must invoke the destroy notifications in the order of @keys.
* Hence, build up the list @old at index @i_keys. */
old[i_keys] = *data;
found_keys++;
remove = TRUE;
break;
}
}
data_elt = datalist_find (d, keys[i_keys], &idx);
if (!data_elt)
continue;
if (!remove)
{
i_data++;
continue;
}
datalist_remove (d, i_data);
/* We must destroy the keys in the order in which they are specified.
* We achieve that here.
*
* Note that even if @keys contains duplicates, we correctly only
* find them once, as we remove the found entry right away. */
old[found_keys++] = *data_elt;
datalist_remove (d, idx);
}
if (found_keys > 0 && datalist_shrink (&d, &d_to_free))
@ -594,13 +775,10 @@ g_data_remove_internal (GData **datalist,
else
g_datalist_unlock (datalist);
if (found_keys > 0)
for (i_keys = 0; i_keys < found_keys; i_keys++)
{
for (i_keys = 0; i_keys < n_keys; i_keys++)
{
if (old[i_keys].destroy)
old[i_keys].destroy (old[i_keys].data);
}
if (old[i_keys].destroy)
old[i_keys].destroy (old[i_keys].data);
}
if (G_UNLIKELY (old_to_free))
@ -803,9 +981,7 @@ g_datalist_id_set_data_full (GData **datalist,
* This is more efficient than calling g_datalist_id_remove_data()
* multiple times in a row.
*
* Before 2.80, @n_keys had to be not larger than 16. Now it can be larger, but
* note that GData does a linear search, so an excessive number of keys will
* perform badly.
* Before 2.80, @n_keys had to be not larger than 16.
*
* Since: 2.74
*/
@ -1228,37 +1404,56 @@ g_datalist_id_replace_data (GData **datalist,
* is not found.
**/
gpointer
g_datalist_get_data (GData **datalist,
const gchar *key)
g_datalist_get_data (GData **datalist,
const gchar *key)
{
GQuark key_id;
GHashTable *index;
gpointer res = NULL;
GDataElt *data_elt;
GData *d;
GDataElt *data, *data_end;
g_return_val_if_fail (datalist != NULL, NULL);
d = g_datalist_lock_and_get (datalist);
if (d)
if (!d)
goto out;
index = datalist_index_get (d);
if (G_LIKELY (!index))
{
data = d->data;
data_end = data + d->len;
while (data < data_end)
{
/* Here we intentionally compare by strings, instead of calling
* g_quark_try_string() first.
*
* See commit 1cceda49b60b ('Make g_datalist_get_data not look up the
* quark').
*/
if (g_strcmp0 (g_quark_to_string (data->key), key) == 0)
{
res = data->data;
break;
}
data++;
}
guint32 i;
for (i = 0; i < d->len; i++)
{
data_elt = &d->data[i];
/* Here we intentionally compare by strings, instead of calling
* g_quark_try_string() first.
*
* See commit 1cceda49b60b ('Make g_datalist_get_data not look up the
* quark').
*/
if (g_strcmp0 (g_quark_to_string (data_elt->key), key) == 0)
{
res = data_elt->data;
goto out;
}
}
goto out;
}
key_id = g_quark_try_string (key);
if (key_id == 0 && key)
goto out;
data_elt = g_hash_table_lookup (index, &key_id);
if (data_elt)
res = data_elt->data;
out:
g_datalist_unlock (datalist);
return res;

View File

@ -800,16 +800,38 @@ g_node_depth_traverse_level (GNode *node,
* efficient than the other orders.
*
* Specifies the type of traversal performed by g_tree_traverse(),
* g_node_traverse() and g_node_find(). The different orders are
* illustrated here:
* g_node_traverse() and g_node_find().
*
* The different orders are illustrated here:
*
* - In order: A, B, C, D, E, F, G, H, I
* ![](Sorted_binary_tree_inorder.svg)
* <picture>
* <source srcset="Sorted_binary_tree_inorder-dark.svg"
* media="(prefers-color-scheme: dark)">
* <img src="Sorted_binary_tree_inorder.svg"
* alt="Sorted binary tree, in-order traversal">
* </picture>
* - Pre order: F, B, A, D, C, E, G, I, H
* ![](Sorted_binary_tree_preorder.svg)
* <picture>
* <source srcset="Sorted_binary_tree_preorder-dark.svg"
* media="(prefers-color-scheme: dark)">
* <img src="Sorted_binary_tree_preorder.svg"
* alt="Sorted binary tree, pre-order traversal">
* </picture>
* - Post order: A, C, E, D, B, H, I, G, F
* ![](Sorted_binary_tree_postorder.svg)
* <picture>
* <source srcset="Sorted_binary_tree_postorder-dark.svg"
* media="(prefers-color-scheme: dark)">
* <img src="Sorted_binary_tree_postorder.svg"
* alt="Sorted binary tree, post-order traversal">
* </picture>
* - Level order: F, B, G, A, D, I, C, E, H
* ![](Sorted_binary_tree_breadth-first_traversal.svg)
* <picture>
* <source srcset="Sorted_binary_tree_breadth-first_traversal-dark.svg"
* media="(prefers-color-scheme: dark)">
* <img src="Sorted_binary_tree_breadth-first_traversal.svg"
* alt="Sorted binary tree, breadth-first level order traversal">
* </picture>
*/
/**

View File

@ -305,6 +305,7 @@ test_datalist_id_remove_multiple_resize (void)
{
GQuark *quarks;
GQuark *quarks2;
gboolean *has;
const guint N = 1000;
const guint PRIME = 1048583u;
guint i;
@ -314,6 +315,7 @@ test_datalist_id_remove_multiple_resize (void)
quarks = g_new (GQuark, N);
quarks2 = g_new (GQuark, N);
has = g_new0 (gboolean, N);
for (i = 0; i < N; i++)
{
@ -322,12 +324,15 @@ test_datalist_id_remove_multiple_resize (void)
}
for (i = 0; i < N; i++)
g_datalist_id_set_data (&list, quarks[i], GINT_TO_POINTER (i));
{
g_datalist_id_set_data (&list, quarks[i], (gpointer) g_quark_to_string (quarks[i]));
has[i] = TRUE;
}
/* Now we perform a list of random operations (remove/add quarks). */
for (i_run = 0; TRUE; i_run++)
{
int MODE = ((guint) g_test_rand_int ()) % 4;
int MODE = ((guint) g_test_rand_int ()) % 6;
guint n;
guint j;
@ -355,9 +360,15 @@ test_datalist_id_remove_multiple_resize (void)
{
j = (j + PRIME) % N;
if (MODE == 0)
g_datalist_id_remove_data (&list, quarks[j]);
{
g_datalist_id_remove_data (&list, quarks[j]);
has[j] = FALSE;
}
else
g_datalist_id_set_data (&list, quarks[j], GINT_TO_POINTER (j));
{
g_datalist_id_set_data (&list, quarks[j], (gpointer) g_quark_to_string (quarks[j]));
has[j] = TRUE;
}
}
break;
case 3:
@ -366,15 +377,59 @@ test_datalist_id_remove_multiple_resize (void)
{
j = (j + PRIME) % N;
quarks2[i] = quarks[j];
has[j] = FALSE;
}
g_datalist_id_remove_multiple (&list, quarks2, n);
break;
case 4:
/* Mode: lookup string via g_datalist_get_data()*/
for (i = 0; i < n; i++)
{
const char *data;
const char *data2;
const char *key;
j = (j + PRIME) % N;
key = g_quark_to_string (quarks[j]);
data = g_datalist_id_get_data (&list, quarks[j]);
data2 = g_datalist_get_data (&list, key);
g_assert_true (data == data2);
if (data)
g_assert_true (data == key);
g_assert_true ((!!data) == has[j]);
}
break;
case 5:
/* Fill/empty the list completely. */
switch (((guint) g_test_rand_int ()) % 5)
{
case 0:
g_datalist_clear (&list);
for (i = 0; i < N; i++)
has[i] = FALSE;
break;
case 1:
for (i = 0; i < N; i++)
{
j = (j + PRIME) % N;
g_datalist_id_set_data (&list, quarks[j], (gpointer) g_quark_to_string (quarks[j]));
has[i] = TRUE;
}
break;
default:
/* Most of the time we do nothing. The case where we fill/empty
* the list entirely is less interesting. */
break;
}
}
}
g_free (quarks);
g_free (quarks2);
g_free (has);
}
static void