feat(functions): Add hashProperty(...) function#1757
feat(functions): Add hashProperty(...) function#1757donmccurdy wants to merge 7 commits intomainfrom
Conversation
|
Would a "shallow hash" also be added here? WeakMap impl
// WeakMap is the key to being GC-friendly. It won't prevent garbage
// collection of objects that are no longer referenced elsewhere.
const cache = new WeakMap();
// A simple counter to generate unique IDs for new objects.
let nextId = 0;
/**
* Hashes a value.
* @param {object} value The value to hash.
* @returns {number} The hash value.
*/
function shallowReferenceHash(value) {
// Check if we have already hashed this exact object reference.
if (cache.has(value)) {
return cache.get(value);
}
// If it's a new object reference, generate a new ID, store it, and return it.
const hash = nextId++;
cache.set(value, hash);
return hash;
}; |
|
Hm, I can't think of a reason that a shallow hash would be needed right now. For the use cases linked above, it would risk not matching deep duplicates. |
216dd9a to
b80086a
Compare
Taking animation samplers as an example, if running it after accessor dedup, then there is no need to do a deep hash of underlying arrays, just hash/compare the reference of accessor. |
|
For that case, I believe we can reuse the same |
Yeah, the idea of a cache is great, but how to manage life cycles of the cache? Like when calling |
|
Calling For the purposes of As to when the cache "should" be invalidated for users or code managing it explicitly: any time a deep-equality check would have changed. Swapping out an accessor for another deeply-equal accessor does not change the hash and wouldn't require a cache reset, but swapping for an accessor with different values would. I suppose it would be possible to prefill the cache with hash values based on some other, custom computation, if one chose to. |
|
Might need that shallow hash after all! Here's a case where test('toHash - circular reference', async (t) => {
// Create a circular reference, nodeA -> nodeB -> skin -> nodeA.
const document = new Document();
const meshNode = document.createNode('Mesh');
const skeletonNode = document.createNode('Skeleton').addChild(meshNode);
const skin = document.createSkin().addJoint(skeletonNode).setSkeleton(skeletonNode);
meshNode.setMesh(document.createMesh()).setSkin(skin);
t.is(typeof meshNode.toHash(), 'number', 'computes hash');
});
// -> Maximum call stack size exceededThat's tricky, because circular references are rare but possible; the only example I know of is skinning. Ideas:
|
b80086a to
1862935
Compare
|
Resolved with a Next steps:
|
hashProperty(prop)returns a hash computed from all attributes and references held by this property, excluding the 'skip' set. Hash collisions are rare, but possible, so hashes alone should not be used to check for equality, but can be combined withProperty#equals.See also: https://en.wikipedia.org/wiki/Merkle_tree
Related:
PR Dependency Tree
This tree was auto-generated by Charcoal