1 /*
2 Copyright (c) 2005, 2021, Oracle and/or its affiliates.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2.0,
6 as published by the Free Software Foundation.
7
8 This program is also distributed with certain software (including
9 but not limited to OpenSSL) that is licensed under separate terms,
10 as designated in a particular file or component or in included license
11 documentation. The authors of MySQL hereby grant you an additional
12 permission to link the program and your derivative works with the
13 separately licensed software that they have included with MySQL.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License, version 2.0, for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include <ndb_global.h>
26 #include "tuppage.hpp"
27 #include <Vector.hpp>
28
29 #define JAM_FILE_ID 428
30
31
32 struct Record
33 {
34 Uint32 idx;
35 Uint32 size;
36 Uint32* data;
37 };
38
39 NdbOut&
operator <<(NdbOut & out,const Record & rec)40 operator <<(NdbOut& out, const Record& rec)
41 {
42 out << "[ idx: " << rec.idx << " sz: " << rec.size << " ]";
43 return out;
44 }
45
46 #define TRACE(x) x
47
48 static
49 bool
cmp(const Uint32 * p1,const Uint32 * p2,Uint32 words)50 cmp(const Uint32 *p1, const Uint32 *p2, Uint32 words)
51 {
52 if(memcmp(p1, p2, 4*words) == 0)
53 return true;
54
55 for(Uint32 i = 0; i<words; i++)
56 printf(" %.8x", p1[i]);
57 printf("\n");
58
59 for(Uint32 i = 0; i<words; i++)
60 printf(" %.8x", p2[i]);
61 printf("\n");
62
63 return false;
64 }
65
66 static
67 void
do_test(int loops,int dist[5])68 do_test(int loops, int dist[5])
69 {
70 fprintf(stderr, "do_test(%d, [ %d %d %d %d %d ])\n",
71 loops,
72 dist[0],
73 dist[1],
74 dist[2],
75 dist[3],
76 dist[4]);
77 int allocated= 0;
78 Record records[8192];
79
80 Tup_varsize_page page, tmp;
81 page.init();
82
83 for(int i = 0; i<loops; i++)
84 {
85 assert(page.high_index + page.insert_pos <= page.DATA_WORDS);
86
87 for(int j = 0; j<allocated; j++)
88 {
89 Record rec= records[j];
90 Uint32* ptr= page.get_ptr(rec.idx);
91 Uint32 pos = page.get_ptr(rec.idx) - page.m_data;
92 if (page.get_entry_len(rec.idx) != rec.size)
93 {
94 ndbout << "INVALID LEN " << j << " " << rec << " pos: " << pos << endl;
95 ndbout << page << endl;
96 abort();
97 }
98
99 if(!cmp(ptr, rec.data, rec.size))
100 {
101 ndbout << "FAILED " << j << " " << rec << " pos: " << pos << endl;
102 ndbout << page << endl;
103 abort();
104 }
105 }
106
107 loop:
108 int op;
109 int rnd= rand() % 100;
110 for(op= 0; op<5; op++)
111 if(rnd < dist[op])
112 break;
113
114 if(allocated == 0)
115 op= 0;
116 if(page.free_space <= 2 && op == 0) goto loop;
117
118 switch(op){
119 case 0: // Alloc
120 {
121 Record rec;
122 rec.size= 1 + (rand() % (page.free_space-1));
123 rec.data = new Uint32[rec.size];
124 for(Uint32 i= 0; i<rec.size; i++)
125 {
126 rec.data[i] = rand();
127 }
128 ndbout << "Alloc hi: " << page.high_index << " (" <<
129 ((rnd < 30) ? "any" :
130 (rnd < 60) ? "dir" :
131 (rnd < 80) ? "exp" : "fail") << ") ";
132 ndbout << rec.size << flush;
133 if (rnd < 30)
134 {
135 rec.idx= page.alloc_record(rec.size, &tmp, 0);
136 }
137 else if (rnd < 60)
138 {
139 // Alloc with id, from directory
140 Vector<Uint32> free;
141 for(Uint32 i = page.high_index - 1; i > 0; i--)
142 {
143 if (page.get_index_word(i) & page.FREE)
144 {
145 free.push_back(i);
146 if (free.size() > 100)
147 break;
148 }
149 }
150 if (free.size())
151 {
152 rec.idx = free[rand() % free.size()];
153 if (page.alloc_record(rec.idx, rec.size, &tmp) != rec.idx)
154 {
155 abort();
156 }
157 }
158 else
159 {
160 rec.idx = page.high_index;
161 if (page.alloc_record(rec.idx, rec.size, &tmp) != rec.idx)
162 {
163 if (rec.size + 1 != page.free_space)
164 abort();
165 delete [] rec.data;
166 ndbout_c(" FAIL");
167 break;
168 }
169 }
170 }
171 else if(rnd < 80)
172 {
173 // Alloc with id, outside of directory
174 rec.idx = page.high_index + (rand() % (page.free_space - rec.size));
175 if (page.alloc_record(rec.idx, rec.size, &tmp) != rec.idx)
176 {
177 abort();
178 }
179 }
180 else
181 {
182 rec.idx = page.high_index + (page.free_space - rec.size) + 1;
183 if (page.alloc_record(rec.idx, rec.size, &tmp) == rec.idx)
184 {
185 abort();
186 }
187 delete [] rec.data;
188 ndbout_c(" FAIL");
189 break;
190 }
191
192 Uint32 pos = page.get_ptr(rec.idx) - page.m_data;
193 ndbout << " -> " << rec.idx
194 << " pos: " << pos << endl;
195 Uint32* ptr= page.get_ptr(rec.idx);
196 memcpy(ptr, rec.data, 4*rec.size);
197 records[allocated++] = rec;
198 break;
199 }
200 case 1: // Free
201 {
202 int no= rand() % allocated;
203 Record rec= records[no];
204 Uint32 pos = page.get_ptr(rec.idx) - page.m_data;
205 ndbout << "Free hi: " << page.high_index << " no: " << no << " idx: " << rec.idx << " pos: " << pos << endl;
206 Uint32* ptr= page.get_ptr(rec.idx);
207 assert(page.get_entry_len(rec.idx) == rec.size);
208 cmp(ptr, rec.data, rec.size);
209 delete[] rec.data;
210 page.free_record(rec.idx, 0);
211
212 for (unsigned k = no; k + 1 < allocated; k++)
213 records[k] = records[k+1];
214 allocated--;
215
216 break;
217 }
218 case 2: // Reorg
219 ndbout << "Reorg" << endl;
220 page.reorg(&tmp);
221 break;
222 case 3:
223 {
224 Uint32 free = page.free_space;
225 if (free <= 2)
226 {
227 goto shrink;
228 }
229 free /= 2;
230 int no = rand() % allocated;
231 Record rec= records[no];
232 ndbout << "Expand no: " << no << " idx: " << rec.idx
233 << " add: " << free << " reorg: "
234 << !page.is_space_behind_entry(rec.idx, free)
235 << endl;
236 if (!page.is_space_behind_entry(rec.idx, free))
237 {
238 Uint32 buffer[8192];
239 Uint32 len = page.get_entry_len(rec.idx);
240 memcpy(buffer, page.get_ptr(rec.idx), 4*len);
241 page.set_entry_len(rec.idx, 0);
242 page.free_space += len;
243 page.reorg(&tmp);
244 memcpy(page.get_free_space_ptr(), buffer, 4*len);
245 page.set_entry_offset(rec.idx, page.insert_pos);
246 free += len;
247 records[no].size = 0;
248 }
249
250 page.grow_entry(rec.idx, free);
251 records[no].size += free;
252 Uint32 *ptr = page.get_ptr(rec.idx);
253 Uint32 *new_data = new Uint32[records[no].size];
254 for(Uint32 i= 0; i<records[no].size; i++)
255 {
256 ptr[i] = new_data[i] = rand();
257 }
258 delete []rec.data;
259 records[no].data = new_data;
260 break;
261 }
262 case 4:
263 {
264 shrink:
265 int no = rand() % allocated;
266 Record rec = records[no];
267 Uint32 sz = rec.size / 2 + 1;
268 ndbout << "Shrink no: " << no << " idx: " << rec.idx << " remove: "
269 << (rec.size - sz) << endl;
270 page.shrink_entry(rec.idx, sz);
271 records[no].size = sz;
272 break;
273 }
274 }
275
276 }
277 ndbout << page << endl;
278 }
279
280 int
main(int argc,char ** argv)281 main(int argc, char **argv)
282 {
283 ndb_init();
284
285 if (argc > 1)
286 {
287 time_t seed = time(0);
288 srand(seed);
289 fprintf(stderr, "srand(%d)\n", seed);
290 }
291 // alloc, free, reorg, grow, shrink
292
293 int t1[] = { 10, 60, 70, 85, 100 };
294 int t2[] = { 30, 60, 70, 85, 100 };
295 int t3[] = { 50, 60, 70, 85, 100 };
296
297 do_test(10000, t1);
298 do_test(10000, t2);
299 do_test(10000, t3);
300
301 return 0;
302 }
303
304 template class Vector<Record>;
305
306 // hp3750
307 struct Signal { Signal(); int foo; };
Signal()308 Signal::Signal(){}
309