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