Coverage Report

Created: 2025-11-16 06:23

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