Computing fingerprints Fingerprints ensure that the encoding and decoding methods agree on the format of a data type. The fingerprints are a function, recursively, of all of the types that a type contains. This creates a potential problem when types could be mutually recursive: we must avoid an infinite recursion. The basic idea is for each type to have a "base" fingerprint, which we'll denote for type "A" as "K_A". K_A is a constant derived from the lcm type description (and it's stored as lcm_struct->hash). We wish to compute the actual fingerprint (or hash), A(), which is a function of all of A's contained types. In addition, so that we can recognize a recursion, the A() function takes an argument, which is a list of the types already visited. E.g., C() indicates that we wish to compute the hash of type C, given that C is a member of type B, which is a member of type A. We avoid recursions by setting C() = 0 if contains C. The contribution of primitive types is handled via the K_A; there is no recursion for them. A small wrinkle arises from the above definitions: if types A, B, and C are mutually recursive, we can have two types with the same hash. This is clearly undesirable. We fix this by making the order of recursion relevant: at each node in the tree, we rotate the value (bitwise) 1 bit to the left. A type that is included at recursion depth N has its contribution rotated by N bits. Note that this mechanism is entirely unnecessary for enumerations (they cannot contain other types); for enumerations, we just use the hash in lcmenum->hash. PSEUDO-CODE ----------- v = compute_hash(type, parents) if type \in parents return 0 v = K_type; for each members m of type v += compute_hash(m, ) return rot_left(v); When encoding/decode a type T, we would use compute_hash(T, <>) as the hash function. EXAMPLE ------- struct A { B b; C c; } struct B { A a; } struct C { B b; } Diagramatically, we can compute their hashes by showing the children of each branch. We use lower case to indicate a terminal leaf (where the leaf is the same class as one of its parents). A B C / \ | | B C A B | | / \ | a B b C A | | / \ a b b c A() = R{K_A + R{K_B}} + R{K_C + R{K_B}}} B() = R{K_B + R{K_A + R{K_C}}} C() = R{K_C + R{K_B + R{K_A}}} Note that without the rotations, B() == C().