text: #include <stdlib.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdbool.h>
  #include <inttypes.h>
  #include <vector>
  #include <time.h>
  
  #include "unit.h"
  
  typedef uint64_t hash_value_t;
  typedef uint32_t hash_t;
  
  static const size_t light_extent_size = 16 * 1024;
  static size_t extents_count = 0;
  
  hash_t
  hash(hash_value_t value)
  {
  	return (hash_t) value;
  }
  
  bool
  equal(hash_value_t v1, hash_value_t v2)
  {
  	return v1 == v2;
  }
  
  bool
  equal_key(hash_value_t v1, hash_value_t v2)
  {
  	return v1 == v2;
  }
  
  #define LIGHT_NAME
  #define LIGHT_DATA_TYPE uint64_t
  #define LIGHT_KEY_TYPE uint64_t
  #define LIGHT_CMP_ARG_TYPE int
  #define LIGHT_EQUAL(a, b, arg) equal(a, b)
  #define LIGHT_EQUAL_KEY(a, b, arg) equal_key(a, b)
  #include "salad/light.h"
  
  inline void *
  my_light_alloc()
  {
  	extents_count++;
  	return malloc(light_extent_size);
  }
  
  inline void
  my_light_free(void *p)
  {
  	extents_count--;
  	free(p);
  }
  
  
  static void
  simple_test()
  {
  	header();
  
  	struct light_core ht;
  	light_create(&ht, light_extent_size, my_light_alloc, my_light_free, 0);
  	std::vector<bool> vect;
  	size_t count = 0;
  	const size_t rounds = 1000;
  	const size_t start_limits = 20;
  	for(size_t limits = start_limits; limits <= 2 * rounds; limits *= 10) {
  		while (vect.size() < limits)
  			vect.push_back(false);
  		for (size_t i = 0; i < rounds; i++) {
  
  			hash_value_t val = rand() % limits;
  			hash_t h = hash(val);
  			hash_t fnd = light_find(&ht, h, val);
  			bool has1 = fnd != light_end;
  			bool has2 = vect[val];
  			assert(has1 == has2);
  			if (has1 != has2) {
  				fail("find key failed!", "true");
  				return;
  			}
  
  			if (!has1) {
  				count++;
  				vect[val] = true;
  				light_insert(&ht, h, val);
  			} else {
  				count--;
  				vect[val] = false;
  				light_delete(&ht, fnd);
  			}
  
  			if (count != ht.count)
  				fail("count check failed!", "true");
  
  			bool identical = true;
  			for (hash_value_t test = 0; test < limits; test++) {
  				if (vect[test]) {
  					if (light_find(&ht, hash(test), test) == light_end)
  						identical = false;
  				} else {
  					if (light_find(&ht, hash(test), test) != light_end)
  						identical = false;
  				}
  			}
  			if (!identical)
  				fail("internal test failed!", "true");
  
  			int check = light_selfcheck(&ht);
  			if (check)
  				fail("internal test failed!", "true");
  		}
  	}
  	light_destroy(&ht);
  
  	footer();
  }
  
  static void
  collision_test()
  {
  	header();
  
  	struct light_core ht;
  	light_create(&ht, light_extent_size, my_light_alloc, my_light_free, 0);
  	std::vector<bool> vect;
  	size_t count = 0;
  	const size_t rounds = 100;
  	const size_t start_limits = 20;
  	for(size_t limits = start_limits; limits <= 2 * rounds; limits *= 10) {
  		while (vect.size() < limits)
  			vect.push_back(false);
  		for (size_t i = 0; i < rounds; i++) {
  
  			hash_value_t val = rand() % limits;
  			hash_t h = hash(val);
  			hash_t fnd = light_find(&ht, h * 1024, val);
  			bool has1 = fnd != light_end;
  			bool has2 = vect[val];
  			assert(has1 == has2);
  			if (has1 != has2) {
  				fail("find key failed!", "true");
  				return;
  			}
  
  			if (!has1) {
  				count++;
  				vect[val] = true;
  				light_insert(&ht, h * 1024, val);
  			} else {
  				count--;
  				vect[val] = false;
  				light_delete(&ht, fnd);
  			}
  
  			if (count != ht.count)
  				fail("count check failed!", "true");
  
  			bool identical = true;
  			for (hash_value_t test = 0; test < limits; test++) {
  				if (vect[test]) {
  					if (light_find(&ht, hash(test) * 1024, test) == light_end)
  						identical = false;
  				} else {
  					if (light_find(&ht, hash(test) * 1024, test) != light_end)
  						identical = false;
  				}
  			}
  			if (!identical)
  				fail("internal test failed!", "true");
  
  			int check = light_selfcheck(&ht);
  			if (check)
  				fail("internal test failed!", "true");
  		}
  	}
  	light_destroy(&ht);
  
  	footer();
  }
  
  static void
  itr_test()
  {
  	header();
  
  	struct light_core ht;
  	light_create(&ht, light_extent_size, my_light_alloc, my_light_free, 0);
  	const size_t rounds = 1000;
  	const size_t start_limits = 20;
  
  	const size_t iterator_count = 16;
  	struct light_iterator itrs[iterator_count];
  	for (size_t i = 0; i < iterator_count; i++)
  		light_itr_begin(&ht, itrs + i);
  	size_t cur_iterator = 0;
  	hash_value_t strage_thing = 0;
  
  	for(size_t limits = start_limits; limits <= 2 * rounds; limits *= 10) {
  		for (size_t i = 0; i < rounds; i++) {
  			hash_value_t val = rand() % limits;
  			hash_t h = hash(val);
  			hash_t fnd = light_find(&ht, h, val);
  
  			if (fnd == light_end) {
  				light_insert(&ht, h, val);
  			} else {
  				light_delete(&ht, fnd);
  			}
  
  			hash_value_t *pval = light_itr_get_and_next(&ht, itrs + cur_iterator);
  			if (pval)
  				strage_thing ^= *pval;
  			if (!pval || (rand() % iterator_count) == 0) {
  				if (rand() % iterator_count) {
  					hash_value_t val = rand() % limits;
  					hash_t h = hash(val);
  					light_itr_key(&ht, itrs + cur_iterator, h, val);
  				} else {
  					light_itr_begin(&ht, itrs + cur_iterator);
  				}
  			}
  
  			cur_iterator++;
  			if (cur_iterator >= iterator_count)
  				cur_iterator = 0;
  		}
  	}
  	light_destroy(&ht);
  
  	if (strage_thing >> 20) {
  		printf("impossible!\n"); // prevent strage_thing to be optimized out
  	}
  
  	footer();
  }
  
  static void
  itr_freeze_check()
  {
  	header();
  
  	const int test_data_size = 1000;
  	hash_value_t comp_buf[test_data_size];
  	const int test_data_mod = 2000;
  	srand(0);
  	struct light_core ht;
  
  	for (int i = 0; i < 10; i++) {
  		light_create(&ht, light_extent_size, my_light_alloc, my_light_free, 0);
  		int comp_buf_size = 0;
  		int comp_buf_size2 = 0;
  		for (int j = 0; j < test_data_size; j++) {
  			hash_value_t val = rand() % test_data_mod;
  			hash_t h = hash(val);
  			light_insert(&ht, h, val);
  		}
  		struct light_iterator itr;
  		light_itr_begin(&ht, &itr);
  		hash_value_t *e;
  		while ((e = light_itr_get_and_next(&ht, &itr))) {
  			comp_buf[comp_buf_size++] = *e;
  		}
  		struct light_iterator itr1;
  		light_itr_begin(&ht, &itr1);
  		light_itr_freeze(&ht, &itr1);
  		struct light_iterator itr2;
  		light_itr_begin(&ht, &itr2);
  		light_itr_freeze(&ht, &itr2);
  		for (int j = 0; j < test_data_size; j++) {
  			hash_value_t val = rand() % test_data_mod;
  			hash_t h = hash(val);
  			light_insert(&ht, h, val);
  		}
  		int tested_count = 0;
  		while ((e = light_itr_get_and_next(&ht, &itr1))) {
  			if (*e != comp_buf[tested_count]) {
  				fail("version restore failed (1)", "true");
  			}
  			tested_count++;
  			if (tested_count > comp_buf_size) {
  				fail("version restore failed (2)", "true");
  			}
  		}
  		light_itr_destroy(&ht, &itr1);
  		for (int j = 0; j < test_data_size; j++) {
  			hash_value_t val = rand() % test_data_mod;
  			hash_t h = hash(val);
  			hash_t pos = light_find(&ht, h, val);
  			if (pos != light_end)
  				light_delete(&ht, pos);
  		}
  
  		tested_count = 0;
  		while ((e = light_itr_get_and_next(&ht, &itr2))) {
  			if (*e != comp_buf[tested_count]) {
  				fail("version restore failed (3)", "true");
  			}
  			tested_count++;
  			if (tested_count > comp_buf_size) {
  				fail("version restore failed (4)", "true");
  			}
  		}
  
  		light_destroy(&ht);
  	}
  
  	footer();
  }
  
  int
  main(int, const char**)
  {
  	srand(time(0));
  	simple_test();
  	collision_test();
  	itr_test();
  	itr_freeze_check();
  	if (extents_count != 0)
  		fail("memory leak!", "true");
  }
  