Coverage Report

Created: 2026-06-13 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/opcache/zend_accelerator_hash.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Zend OPcache                                                         |
4
   +----------------------------------------------------------------------+
5
   | Copyright © The PHP Group and Contributors.                          |
6
   +----------------------------------------------------------------------+
7
   | This source file is subject to the Modified BSD License that is      |
8
   | bundled with this package in the file LICENSE, and is available      |
9
   | through the World Wide Web at <https://www.php.net/license/>.        |
10
   |                                                                      |
11
   | SPDX-License-Identifier: BSD-3-Clause                                |
12
   +----------------------------------------------------------------------+
13
   | Authors: Andi Gutmans <andi@php.net>                                 |
14
   |          Zeev Suraski <zeev@php.net>                                 |
15
   |          Stanislav Malyshev <stas@zend.com>                          |
16
   |          Dmitry Stogov <dmitry@php.net>                              |
17
   +----------------------------------------------------------------------+
18
*/
19
20
#include "ZendAccelerator.h"
21
#include "zend_accelerator_hash.h"
22
#include "zend_hash.h"
23
#include "zend_shared_alloc.h"
24
25
/* Generated on an Octa-ALPHA 300MHz CPU & 2.5GB RAM monster */
26
static const uint32_t prime_numbers[] =
27
  {5, 11, 19, 53, 107, 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987, 262237, 524521, 1048793 };
28
static const uint32_t num_prime_numbers = sizeof(prime_numbers) / sizeof(uint32_t);
29
30
void zend_accel_hash_clean(zend_accel_hash *accel_hash)
31
0
{
32
0
  accel_hash->num_entries = 0;
33
0
  accel_hash->num_direct_entries = 0;
34
0
  memset(accel_hash->hash_table, 0, sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries);
35
0
}
36
37
void zend_accel_hash_init(zend_accel_hash *accel_hash, uint32_t hash_size)
38
16
{
39
16
  uint32_t i;
40
41
192
  for (i=0; i<num_prime_numbers; i++) {
42
192
    if (hash_size <= prime_numbers[i]) {
43
16
      hash_size = prime_numbers[i];
44
16
      break;
45
16
    }
46
192
  }
47
48
16
  accel_hash->num_entries = 0;
49
16
  accel_hash->num_direct_entries = 0;
50
16
  accel_hash->max_num_entries = hash_size;
51
52
  /* set up hash pointers table */
53
16
  accel_hash->hash_table = zend_shared_alloc(sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries);
54
16
  if (!accel_hash->hash_table) {
55
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!");
56
0
    return;
57
0
  }
58
59
  /* set up hash values table */
60
16
  accel_hash->hash_entries = zend_shared_alloc(sizeof(zend_accel_hash_entry)*accel_hash->max_num_entries);
61
16
  if (!accel_hash->hash_entries) {
62
0
    zend_accel_error_noreturn(ACCEL_LOG_FATAL, "Insufficient shared memory!");
63
0
    return;
64
0
  }
65
16
  memset(accel_hash->hash_table, 0, sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries);
66
16
}
67
68
/* Returns NULL if hash is full
69
 * Returns pointer the actual hash entry on success
70
 * key needs to be already allocated as it is not copied
71
 */
72
zend_accel_hash_entry* zend_accel_hash_update(zend_accel_hash *accel_hash, zend_string *key, bool indirect, void *data)
73
50.2k
{
74
50.2k
  zend_ulong hash_value;
75
50.2k
  zend_ulong index;
76
50.2k
  zend_accel_hash_entry *entry;
77
50.2k
  zend_accel_hash_entry *indirect_bucket = NULL;
78
79
50.2k
  if (indirect) {
80
0
    indirect_bucket = (zend_accel_hash_entry*)data;
81
0
    while (indirect_bucket->indirect) {
82
0
      indirect_bucket = (zend_accel_hash_entry*)indirect_bucket->data;
83
0
    }
84
0
  }
85
86
50.2k
  hash_value = zend_string_hash_val(key);
87
50.2k
#ifndef ZEND_WIN32
88
50.2k
  hash_value ^= ZCG(root_hash);
89
50.2k
#endif
90
50.2k
  index = hash_value % accel_hash->max_num_entries;
91
92
  /* try to see if the element already exists in the hash */
93
50.2k
  entry = accel_hash->hash_table[index];
94
50.2k
  while (entry) {
95
50.2k
    if (entry->hash_value == hash_value
96
50.2k
     && zend_string_equals(entry->key, key)) {
97
98
50.2k
      if (entry->indirect) {
99
0
        if (indirect_bucket) {
100
0
          entry->data = indirect_bucket;
101
0
        } else {
102
0
          ((zend_accel_hash_entry*)entry->data)->data = data;
103
0
        }
104
50.2k
      } else {
105
50.2k
        if (indirect_bucket) {
106
0
          accel_hash->num_direct_entries--;
107
0
          entry->data = indirect_bucket;
108
0
          entry->indirect = 1;
109
50.2k
        } else {
110
50.2k
          entry->data = data;
111
50.2k
        }
112
50.2k
      }
113
50.2k
      return entry;
114
50.2k
    }
115
0
    entry = entry->next;
116
0
  }
117
118
  /* Does not exist, add a new entry */
119
3
  if (accel_hash->num_entries == accel_hash->max_num_entries) {
120
0
    return NULL;
121
0
  }
122
123
3
  entry = &accel_hash->hash_entries[accel_hash->num_entries++];
124
3
  if (indirect) {
125
0
    entry->data = indirect_bucket;
126
0
    entry->indirect = 1;
127
3
  } else {
128
3
    accel_hash->num_direct_entries++;
129
3
    entry->data = data;
130
3
    entry->indirect = 0;
131
3
  }
132
3
  entry->hash_value = hash_value;
133
3
  entry->key = key;
134
3
  entry->next = accel_hash->hash_table[index];
135
3
  accel_hash->hash_table[index] = entry;
136
3
  return entry;
137
3
}
138
139
static zend_always_inline void* zend_accel_hash_find_ex(const zend_accel_hash *accel_hash, zend_string *key, bool data)
140
349k
{
141
349k
  zend_ulong index;
142
349k
  zend_accel_hash_entry *entry;
143
349k
  zend_ulong hash_value;
144
145
349k
  hash_value = zend_string_hash_val(key);
146
349k
#ifndef ZEND_WIN32
147
349k
  hash_value ^= ZCG(root_hash);
148
349k
#endif
149
349k
  index = hash_value % accel_hash->max_num_entries;
150
151
349k
  entry = accel_hash->hash_table[index];
152
349k
  while (entry) {
153
296k
    if (entry->hash_value == hash_value
154
296k
     && zend_string_equals(entry->key, key)) {
155
296k
      if (entry->indirect) {
156
0
        if (data) {
157
0
          return ((zend_accel_hash_entry*)entry->data)->data;
158
0
        } else {
159
0
          return entry->data;
160
0
        }
161
296k
      } else {
162
296k
        if (data) {
163
246k
          return entry->data;
164
246k
        } else {
165
50.2k
          return entry;
166
50.2k
        }
167
296k
      }
168
296k
    }
169
0
    entry = entry->next;
170
0
  }
171
52.8k
  return NULL;
172
349k
}
173
174
/* Returns the data associated with key on success
175
 * Returns NULL if data doesn't exist
176
 */
177
void* zend_accel_hash_find(const zend_accel_hash *accel_hash, zend_string *key)
178
296k
{
179
296k
  return zend_accel_hash_find_ex(accel_hash, key, true);
180
296k
}
181
182
/* Returns the hash entry associated with key on success
183
 * Returns NULL if it doesn't exist
184
 */
185
zend_accel_hash_entry* zend_accel_hash_find_entry(const zend_accel_hash *accel_hash, zend_string *key)
186
52.5k
{
187
52.5k
  return (zend_accel_hash_entry *)zend_accel_hash_find_ex(accel_hash, key, false);
188
52.5k
}
189
190
zend_result zend_accel_hash_unlink(zend_accel_hash *accel_hash, zend_string *key)
191
0
{
192
0
  zend_ulong hash_value;
193
0
  zend_ulong index;
194
0
  zend_accel_hash_entry *entry, *last_entry=NULL;
195
196
0
  hash_value = zend_string_hash_val(key);
197
0
#ifndef ZEND_WIN32
198
0
  hash_value ^= ZCG(root_hash);
199
0
#endif
200
0
  index = hash_value % accel_hash->max_num_entries;
201
202
0
  entry = accel_hash->hash_table[index];
203
0
  while (entry) {
204
0
    if (entry->hash_value == hash_value
205
0
     && zend_string_equals(entry->key, key)) {
206
0
      if (!entry->indirect) {
207
0
        accel_hash->num_direct_entries--;
208
0
      }
209
0
      if (last_entry) {
210
0
        last_entry->next = entry->next;
211
0
      } else {
212
0
        accel_hash->hash_table[index] = entry->next;
213
0
      }
214
0
      return SUCCESS;
215
0
    }
216
0
    last_entry = entry;
217
0
    entry = entry->next;
218
0
  }
219
0
  return FAILURE;
220
0
}