// A universal hash function that can handle objects of any type. // // The way it's currently implemented, two objects will have the same hash if // all their non-address fields (all the way down) expand to the same sequence // of scalar values. In particular, a container with all zero addresses hashes // to 0. Hopefully this won't be an issue because we are usually hashing // objects of a single type in any given hash table. // // Based on http://burtleburtle.net/bob/hash/hashfaq.html :(before "End Primitive Recipe Declarations") HASH, :(before "End Primitive Recipe Numbers") put(Recipe_ordinal, "hash", HASH); :(before "End Primitive Recipe Checks") case HASH: { if (SIZE(inst.ingredients) != 1) { raise << maybe(get(Recipe, r).name) << "'hash' takes exactly one ingredient rather than '" << inst.original_string << "'\n" << end(); break; } break; } :(before "End Primitive Recipe Implementations") case HASH: { const reagent& input = current_instruction().ingredients.at(0); products.resize(1); products.at(0).push_back(hash(0, input)); break; } //: in all the code below, the intermediate results of hashing are threaded through 'h' :(code) size_t hash(size_t h, reagent/*copy*/ r) { canonize(r); if (is_mu_text(r)) // optimization return hash_mu_text(h, r); else if (is_mu_address(r)) return hash_mu_address(h, r); else if (is_mu_scalar(r)) return hash_mu_scalar(h, r); else if (is_mu_array(r)) return hash_mu_array(h, r); else if (is_mu_container(r)) return hash_mu_container(h, r); else if (is_mu_exclusive_container(r)) return hash_mu_exclusive_container(h, r); assert(false); } size_t hash_mu_scalar(size_t h, const reagent& r) { double input = is_literal(r) ? r.value : get_or_insert(Memory, r.value); return hash_iter(h, static_cast(input)); } size_t hash_mu_address(size_t h, reagent& r) { if (r.value == 0) return 0; trace(9999, "mem") << "location " << r.value << " is " << no_scientific(get_or_insert(Memory, r.value)) << end(); r.set_value(get_or_insert(Memory, r.value)); if (r.value != 0) { trace(9999, "mem") << "skipping refcount at " << r.value << end(); r.set_value(r.value+1); // skip refcount } drop_from_type(r, "address"); return hash(h, r); } size_t hash_mu_text(size_t h, const reagent& r) { string input = read_mu_text(get_or_insert(Memory, r.value)); for (int i = 0; i < SIZE(input); ++i) { h = hash_iter(h, static_cast(input.at(i))); //? cerr << i << ": " << h << '\n'; } return h; } size_t hash_mu_array(size_t h, const reagent& r) { int size = get_or_insert(Memory, r.value); reagent/*copy*/ elem = r; delete elem.type; elem.type = copy_array_element(r.type); for (int i=0, address = r.value+1; i < size; ++i, address += size_of(elem)) { reagent/*copy*/ tmp = elem; tmp.set_value(address); h = hash(h, tmp); //? cerr << i << " (" << address << "): " << h << '\n'; } return h; } size_t hash_mu_container(size_t h, const reagent& r) { type_info& info = get(Type, root_type(r.type)->value); int address = r.value; int offset = 0; for (int i = 0; i < SIZE(info.elements); ++i) { reagent/*copy*/ element = element_type(r.typ
../../ranger/config/commands.py
{ raise << maybe(get(Recipe, r).name) << "'hash_old' takes exactly one ingredient rather than '" << inst.original_string << "'\n" << end(); break; } if (!is_mu_text(inst.ingredients.at(0))) { raise << maybe(get(Recipe, r).name) << "'hash_old' currently only supports texts (address array character), but got '" << inst.ingredients.at(0).original_string << "'\n" << end(); break; } break; } :(before "End Primitive Recipe Implementations") case HASH_OLD: { string input = read_mu_text(ingredients.at(0).at(0)); size_t h = 0 ; for (int i = 0; i < SIZE(input); ++i) { h += static_cast(input.at(i)); h += (h<<10); h ^= (h>>6); h += (h<<3); h ^= (h>>11); h += (h<<15); } products.resize(1); products.at(0).push_back(h); break; }