diff --git a/src/Amalgam/GeneralizedDistance.h b/src/Amalgam/GeneralizedDistance.h index 9a49ddff..c9053e83 100644 --- a/src/Amalgam/GeneralizedDistance.h +++ b/src/Amalgam/GeneralizedDistance.h @@ -1021,7 +1021,7 @@ class RepeatedGeneralizedDistanceEvaluator //represented by precomputedRemainingIdenticalDistanceTerm EFDT_REMAINING_IDENTICAL_PRECOMPUTED, //everything is precomputed from interned values that are looked up - EFDT_NUMERIC_PRECOMPUTED, + EFDT_UNIVERSALLY_INTERNED_PRECOMPUTED, //continuous without cycles, but everything is always numeric EFDT_CONTINUOUS_UNIVERSALLY_NUMERIC, //continuous without cycles, may contain nonnumeric data @@ -1202,8 +1202,8 @@ class RepeatedGeneralizedDistanceEvaluator return featureData[index].internedNumberIndexToNumberValue != nullptr; } - //returns the precomputed distance term for the interned number with intern_value_index - __forceinline double ComputeDistanceTermNumberInternedPrecomputed(size_t intern_value_index, size_t index, bool high_accuracy) + //returns the precomputed distance term for the interned value with intern_value_index + __forceinline double ComputeDistanceTermInternedPrecomputed(size_t intern_value_index, size_t index, bool high_accuracy) { return featureData[index].internedDistanceTerms[intern_value_index].GetValue(high_accuracy); } diff --git a/src/Amalgam/SeparableBoxFilterDataStore.cpp b/src/Amalgam/SeparableBoxFilterDataStore.cpp index 868022d6..45fbfe07 100644 --- a/src/Amalgam/SeparableBoxFilterDataStore.cpp +++ b/src/Amalgam/SeparableBoxFilterDataStore.cpp @@ -1480,7 +1480,7 @@ void SeparableBoxFilterDataStore::PopulateTargetValueAndLabelIndex(RepeatedGener if(column_data->internedNumberValues.valueInterningEnabled) { if(all_values_numeric) - effective_feature_type = RepeatedGeneralizedDistanceEvaluator::EFDT_NUMERIC_PRECOMPUTED; + effective_feature_type = RepeatedGeneralizedDistanceEvaluator::EFDT_UNIVERSALLY_INTERNED_PRECOMPUTED; else effective_feature_type = RepeatedGeneralizedDistanceEvaluator::EFDT_CONTINUOUS_NUMERIC_PRECOMPUTED; diff --git a/src/Amalgam/SeparableBoxFilterDataStore.h b/src/Amalgam/SeparableBoxFilterDataStore.h index e0579faa..320a165f 100644 --- a/src/Amalgam/SeparableBoxFilterDataStore.h +++ b/src/Amalgam/SeparableBoxFilterDataStore.h @@ -755,10 +755,10 @@ class SeparableBoxFilterDataStore query_feature_index, high_accuracy); } - case RepeatedGeneralizedDistanceEvaluator::EFDT_NUMERIC_PRECOMPUTED: + case RepeatedGeneralizedDistanceEvaluator::EFDT_UNIVERSALLY_INTERNED_PRECOMPUTED: { auto &feature_attribs = r_dist_eval.distEvaluator->featureAttribs[query_feature_index]; - return r_dist_eval.ComputeDistanceTermNumberInternedPrecomputed( + return r_dist_eval.ComputeDistanceTermInternedPrecomputed( GetValue(entity_index, feature_attribs.featureIndex).indirectionIndex, query_feature_index, high_accuracy); } @@ -791,7 +791,7 @@ class SeparableBoxFilterDataStore auto &feature_attribs = r_dist_eval.distEvaluator->featureAttribs[query_feature_index]; auto &column_data = columnData[feature_attribs.featureIndex]; if(column_data->numberIndices.contains(entity_index)) - return r_dist_eval.ComputeDistanceTermNumberInternedPrecomputed( + return r_dist_eval.ComputeDistanceTermInternedPrecomputed( GetValue(entity_index, feature_attribs.featureIndex).indirectionIndex, query_feature_index, high_accuracy); else return r_dist_eval.distEvaluator->ComputeDistanceTermKnownToUnknown(query_feature_index, high_accuracy); diff --git a/src/Amalgam/amlg_code/full_test.amlg b/src/Amalgam/amlg_code/full_test.amlg index 8254f417..2575bdeb 100644 --- a/src/Amalgam/amlg_code/full_test.amlg +++ b/src/Amalgam/amlg_code/full_test.amlg @@ -1938,9 +1938,9 @@ (print (difference_entities "DiffEntity1" "DiffEntity2")) (let (assoc new_entity - (call (difference_entities "DiffEntity1" "DiffEntity2") (assoc _ "DiffEntity1"))) + (call (difference_entities "DiffEntity1" "DiffEntity2") (assoc _ "DiffEntity1"))) (print "new_entity: " new_entity "\n") - (print "new_entity root: " (retrieve_entity_root new_entity)) + (print "new_entity root: " (retrieve_entity_root new_entity)) (print "DiffEntityChild1 root:\n" (retrieve_entity_root (list new_entity "DiffEntityChild1"))) (print "contained_entities new_entity: " (contained_entities new_entity)) (print "difference between DiffEntity2 and new_entity:\n" (difference_entities "DiffEntity2" new_entity)) @@ -1967,10 +1967,10 @@ (print (difference_entities (list "DiffContainer" "DiffEntity1") (list "DiffContainer" "DiffEntity2") )) (let (assoc new_entity - (call (difference_entities (list "DiffContainer" "DiffEntity1") (list "DiffContainer" "DiffEntity2") ) + (call (difference_entities (list "DiffContainer" "DiffEntity1") (list "DiffContainer" "DiffEntity2") ) (assoc _ (list "DiffContainer" "DiffEntity1") new_entity "DiffContainerReconstructed"))) (print "new_entity: " new_entity "\n") - (print "new_entity root: " (retrieve_entity_root new_entity)) + (print "new_entity root: " (retrieve_entity_root new_entity)) (print "DiffEntityChild1 root:\n" (retrieve_entity_root (list new_entity "DiffEntityChild1"))) (print "contained_entities new_entity: " (contained_entities new_entity)) (print "difference between DiffContainer and DiffEntity2:\n" (difference_entities (list "DiffContainer" "DiffEntity2") new_entity )) diff --git a/src/Amalgam/amlg_code/test.amlg b/src/Amalgam/amlg_code/test.amlg index 7ae49167..1dbfde1a 100644 --- a/src/Amalgam/amlg_code/test.amlg +++ b/src/Amalgam/amlg_code/test.amlg @@ -1,50 +1,12 @@ (seq -(if 0 (seq - (print - (declare - {_ (null)} - (replace - _ - [] - (lambda - {o 6} - ) - ) - ) - "\n") - (conclude) - ) + (create_entities "MergeEntity1" (lambda (associate "a" 3 "b" 4)) ) + ;(create_entities (list "MergeEntity1" "MergeEntityChild1") (lambda (associate "x" 3 "y" 4)) ) + ;(create_entities (list "MergeEntity1" "MergeEntityChild2") (lambda (associate "p" 3 "q" 4)) ) + ;(create_entities (list "MergeEntity1") (lambda (associate "E" 3 "F" 4)) ) + ;(create_entities (list "MergeEntity1") (lambda (associate "e" 3 "f" 4 "g" 5 "h" 6)) ) + + (store_entity "amlg_code/module_test_c.caml" "MergeEntity1") + (load_entity "amlg_code/module_test_c.caml" "MergeEntity1Decompressed") + (print "Compression difference: [" (difference_entities "MergeEntity1" "MergeEntity1Decompressed") "]\n") ) - - (create_entities "DiffEntity1" (lambda (assoc "c" 3 "b" 4)) ) - (create_entities (list "DiffEntity1" "DiffEntityChild1") (lambda (assoc "x" 3 "y" 4 "z" 6)) ) - (create_entities (list "DiffEntity1" "DiffEntityChild1" "DiffEntityChild2") (lambda (assoc "p" 3 "q" 4 "u" 5 "v" 6 "w" 7)) ) - (create_entities (list "DiffEntity1" "DiffEntityChild1" "DiffEntityChild2" "DiffEntityChild3") (lambda (assoc "e" 3 "p" 4 "a" 5 "o" 6 "w" 7)) ) - (create_entities (list "DiffEntity1" "OnlyIn1") (lambda (assoc "m" 4)) ) - (create_entities (list "DiffEntity1") (lambda (assoc "E" 3 "F" 4)) ) - (create_entities (list "DiffEntity1") (lambda (assoc "e" 3 "f" 4 "g" 5 "h" 6)) ) - - (create_entities "DiffEntity2" (lambda (assoc "c" 3 "b" 4)) ) - (create_entities (list "DiffEntity2" "DiffEntityChild1") (lambda (assoc "x" 3 "y" 4 "z" 5)) ) - (create_entities (list "DiffEntity2" "DiffEntityChild1" "DiffEntityChild2") (lambda (assoc "p" 3 "q" 4 "u" 5 "v" 6 "w" 7)) ) - (create_entities (list "DiffEntity2" "DiffEntityChild1" "DiffEntityChild2" "DiffEntityChild3") (lambda (assoc "e" 3 "p" 4 "a" 5 "o" 6 "w" 7)) ) - (create_entities (list "DiffEntity2" "OnlyIn2") (lambda (assoc "o" 6)) ) - (create_entities (list "DiffEntity2") (lambda (assoc "E" 3 "F" 4 "G" 5 "H" 6)) ) - (create_entities (list "DiffEntity2") (lambda (assoc "e" 3 "f" 4)) ) - - (print (contained_entities "DiffEntity2")) - - (print (difference_entities "DiffEntity1" "DiffEntity2")) - - (let (assoc new_entity - (call (difference_entities "DiffEntity1" "DiffEntity2") (assoc _ "DiffEntity1"))) - (print "new_entity: " new_entity "\n") - (print "new_entity root: " (retrieve_entity_root new_entity)) - (print "DiffEntityChild1 root:\n" (retrieve_entity_root (list new_entity "DiffEntityChild1"))) - (print "contained_entities new_entity: " (contained_entities new_entity)) - (print "difference between DiffEntity2 and new_entity:\n" (difference_entities "DiffEntity2" new_entity)) - ;(print "DiffEntity2:\n" (flatten_entity "DiffEntity2")) - ;(print "new_entity:\n" (flatten_entity new_entity)) - ) -) \ No newline at end of file diff --git a/src/Amalgam/evaluablenode/EvaluableNodeTreeFunctions.cpp b/src/Amalgam/evaluablenode/EvaluableNodeTreeFunctions.cpp index cb77738f..017cdda0 100644 --- a/src/Amalgam/evaluablenode/EvaluableNodeTreeFunctions.cpp +++ b/src/Amalgam/evaluablenode/EvaluableNodeTreeFunctions.cpp @@ -77,82 +77,6 @@ std::vector CustomEvaluableNodeOrderedChildNodesSort(std::vecto return list_copy_2; } -void TraverseToEntityViaEvaluableNodeIDPath(Entity *container, EvaluableNode *id_path, - Entity *&relative_entity_container, StringInternRef &id, Entity *&relative_entity) -{ - //TODO 10975: remove this function, deprecated by TraverseToEntityReferenceAndContainerViaEvaluableNodeIDPath - relative_entity_container = nullptr; - id = StringInternPool::NOT_A_STRING_ID; - - if(container == nullptr) - { - relative_entity = nullptr; - return; - } - - relative_entity = container; - - if(EvaluableNode::IsEmptyNode(id_path)) - return; - - auto &ocn = id_path->GetOrderedChildNodes(); - - //size of the entity list excluding trailing nulls - size_t non_null_size = ocn.size(); - for(; non_null_size > 0; non_null_size--) - { - if(!EvaluableNode::IsNull(ocn[non_null_size - 1])) - break; - } - - if(non_null_size == 0) - { - //if empty list, return the container itself - if(id_path->GetType() == ENT_LIST) - return; - - //if the string doesn't exist, then there can't be an entity with that name - id.SetIDWithReferenceHandoff(EvaluableNode::ToStringIDWithReference(id_path)); - - relative_entity = container->GetContainedEntity(id); - relative_entity_container = container; - return; - } - - //if traversing, then it needs to be a list, otherwise not a valid entity - if(id_path->GetType() != ENT_LIST) - { - relative_entity = nullptr; - return; - } - - relative_entity_container = container; - - for(size_t i = 0; i < non_null_size; i++) - { - EvaluableNode *cn = ocn[i]; - - //null means current entity wherever it is in the traversal - if(EvaluableNode::IsNull(cn)) - continue; - - //if the string doesn't exist, then there can't be an entity with that name - id.SetIDWithReferenceHandoff(EvaluableNode::ToStringIDWithReference(cn)); - relative_entity = relative_entity_container->GetContainedEntity(id); - - //if entity doesn't exist, exit gracefully - if(relative_entity == nullptr) - return; - - //if last reference, use destination type - if(i + 1 == non_null_size) - return; - - //create new read lock and overwrite existing to walk down the list - relative_entity_container = relative_entity; - } -} - EvaluableNode *GetTraversalIDPathFromAToB(EvaluableNodeManager *enm, Entity *a, Entity *b) { //shouldn't happen, but check diff --git a/src/Amalgam/evaluablenode/EvaluableNodeTreeFunctions.h b/src/Amalgam/evaluablenode/EvaluableNodeTreeFunctions.h index cba00e48..30df1b27 100644 --- a/src/Amalgam/evaluablenode/EvaluableNodeTreeFunctions.h +++ b/src/Amalgam/evaluablenode/EvaluableNodeTreeFunctions.h @@ -35,98 +35,232 @@ class CustomEvaluableNodeComparator //returns a newly sorted list std::vector CustomEvaluableNodeOrderedChildNodesSort(std::vector &list, CustomEvaluableNodeComparator &cenc); -//Starts at the container specified and traverses the id path specified, finding the relative Entity from container -// if id_path is nullptr, then it will set relative_entity to the container itself, leaving relative_entity_container to nullptr -// if id_path is invalid or container is nullptr, then it will set both relative_entity and relative_entity_container to nullptr -// if id_path is any form of a list, then it will treat the ids as a sequence of subcontainers -// otherwise the id_path is transformed to a string and used as an id -//sets relative_entity_container to the base entity found, sets id to the value of the id relative to the base, and relative_entity to the entity being pointed to -// if the path exists (as in a destination of where to put an entity) but the target entity does not, then relative_entity_container may be a valid reference and relative_entity may be nullptr -//Note that id is allocated in the string_intern_pool, and the caller is responsible for freeing the allocation -void TraverseToEntityViaEvaluableNodeIDPath(Entity *container, EvaluableNode *id_path, Entity *&relative_entity_container, StringInternRef &id, Entity *&relative_entity); - -//Starts at the container specified and traverses the id path specified, finding the relative Entity to from_entity -//returns a reference of the entity specified by the id path followed by a reference to its container -//if id_path does not exist or is invalid then returns nullptr for both -//if id_path specifies the entity in from_entity, then it returns a reference to it -//if dest_sid_with_reference is not null, it will assume it is a location to store a string id with reference -// that must be managed by caller -template -std::pair -TraverseToExistingEntityReferenceAndContainerViaEvaluableNodeIDPath( - Entity *from_entity, EvaluableNode *id_path, - StringInternPool::StringID *dest_sid_with_reference = nullptr) +class EvaluableNodeIDPathTraverser { - //if the destination sid is requested, initialize it - if(dest_sid_with_reference != nullptr) - *dest_sid_with_reference = string_intern_pool.NOT_A_STRING_ID; +public: + inline EvaluableNodeIDPathTraverser() + : idPath(nullptr), idPathEntries(nullptr), curIndex(0) + { } + + //calls AnalyzeIDPath with the same parameters + inline EvaluableNodeIDPathTraverser(EvaluableNode *id_path, bool has_destination_id) + { + AnalyzeIDPath(id_path, has_destination_id); + } + + //populates attributes based on the id_path + //if has_destination_id, then it will leave one id at the end for the destination + void AnalyzeIDPath(EvaluableNode *id_path, bool has_destination_id) + { + idPath = id_path; + idPathEntries = &idPath->GetOrderedChildNodes(); + curIndex = 0; + containerIdIndex = 0; + entityIdIndex = 0; + lastIdIndex = 0; + + //size of the entity list excluding trailing nulls + size_t non_null_size = idPathEntries->size(); + while(non_null_size > 0 && EvaluableNode::IsNull((*idPathEntries)[non_null_size - 1])) + non_null_size--; + + //if no entities, nothing to traverse + if(non_null_size == 0) + { + idPathEntries = nullptr; + return; + } - //string id to use throughout various flows - StringInternPool::StringID sid = string_intern_pool.NOT_A_STRING_ID; + //find first index + while(curIndex < non_null_size && EvaluableNode::IsNull((*idPathEntries)[curIndex])) + curIndex++; - if(from_entity == nullptr) - return std::make_pair(EntityReferenceType(nullptr), ContainerEntityReferenceType(nullptr)); + lastIdIndex = non_null_size - 1; + entityIdIndex = lastIdIndex; - if(EvaluableNode::IsEmptyNode(id_path)) - return std::make_pair(EntityReferenceType(from_entity), ContainerEntityReferenceType(nullptr)); + if(has_destination_id) + { + //walk down to find the entity id + while(entityIdIndex > curIndex && EvaluableNode::IsNull((*idPathEntries)[entityIdIndex - 1])) + entityIdIndex--; + } - if(id_path->GetType() != ENT_LIST) + //index of the target entity's container's id; start at curIndex, + //and if there's room, try to work downward to find the id previous to entityIdIndex + //if there's nothing between, it won't execute, or it will set them back to being the same + containerIdIndex = curIndex; + if(entityIdIndex > curIndex) + { + containerIdIndex = entityIdIndex - 1; + while(containerIdIndex > curIndex && EvaluableNode::IsNull((*idPathEntries)[containerIdIndex - 1])) + containerIdIndex--; + } + } + + constexpr bool IsContainer() + { return (curIndex == containerIdIndex); } + + constexpr bool IsEntity() + { return (curIndex == entityIdIndex); } + + constexpr bool IsLastIndex() + { return (curIndex == lastIdIndex); } + + inline void AdvanceIndex() { - //get the string id, get a reference if returning it - if(dest_sid_with_reference == nullptr) - sid = EvaluableNode::ToStringIDIfExists(id_path); - else - sid = *dest_sid_with_reference = EvaluableNode::ToStringIDWithReference(id_path); + do + { + //advance to next step + curIndex++; + } while(curIndex < entityIdIndex && EvaluableNode::IsNull((*idPathEntries)[curIndex])); + } - //need to lock the container first - ContainerEntityReferenceType container_reference(from_entity); - return std::make_pair(EntityReferenceType(from_entity->GetContainedEntity(sid)), - std::move(container_reference)); + //gets the current ID, nullptr if out of ids + inline EvaluableNode *GetCurId() + { + if(idPathEntries == nullptr || curIndex > entityIdIndex) + return nullptr; + return (*idPathEntries)[curIndex]; } - auto &ocn = id_path->GetOrderedChildNodes(); + //the node for the id path and a pointer to its ordered child nodes + EvaluableNode *idPath; + std::vector *idPathEntries; - //size of the entity list excluding trailing nulls - size_t non_null_size = ocn.size(); - while(non_null_size > 0 && EvaluableNode::IsNull(ocn[non_null_size - 1])) - non_null_size--; + //current index in idPath + size_t curIndex; - //if empty list, return the entity itself - if(non_null_size == 0) - return std::make_pair(EntityReferenceType(from_entity), ContainerEntityReferenceType(nullptr)); + //index of the container of the target entity in idPath + size_t containerIdIndex; - //index of the id that will be used for the target entity - size_t target_entity_id_index = non_null_size - 1; + //index of the target entity entity in idPath + size_t entityIdIndex; - //find first index - size_t cur_index = 0; - while(cur_index < non_null_size && EvaluableNode::IsNull(ocn[cur_index])) - cur_index++; + //index of the last entity id, if applicable + size_t lastIdIndex; +}; - //if there's only one valid entry in the list, retrieve it - if(cur_index == target_entity_id_index) +template +std::pair +TraverseToEntityReferenceAndContainerViaEvaluableNodeID(Entity *from_entity, + EvaluableNode *id_node, + StringInternRef *dest_sid_ref) +{ + if(EvaluableNode::IsEmptyNode(id_node)) + return std::make_pair(EntityReferenceType(from_entity), EntityReferenceType(nullptr)); + + //get the string id, get a reference if returning it + if(dest_sid_ref == nullptr) { - //get the string id, get a reference if returning it - if(dest_sid_with_reference == nullptr) - sid = EvaluableNode::ToStringIDIfExists(ocn[cur_index]); - else - sid = *dest_sid_with_reference = EvaluableNode::ToStringIDWithReference(ocn[cur_index]); + StringInternPool::StringID sid = EvaluableNode::ToStringIDIfExists(id_node); //need to lock the container first - ContainerEntityReferenceType container_reference(from_entity); + EntityReferenceType container_reference(from_entity); return std::make_pair(EntityReferenceType(from_entity->GetContainedEntity(sid)), std::move(container_reference)); } + else + { + StringInternPool::StringID sid = EvaluableNode::ToStringIDWithReference(id_node); + + //if there exists an entity with sid, then return it + Entity *container = from_entity->GetContainedEntity(sid); + if(container != nullptr) + { + string_intern_pool.DestroyStringReference(sid); + return std::make_pair(EntityReferenceType(nullptr), EntityReferenceType(container)); + } + + dest_sid_ref->SetIDWithReferenceHandoff(sid); + return std::make_pair(EntityReferenceType(nullptr), EntityReferenceType(from_entity)); + } +} + +template +std::pair +TraverseToEntityReferenceAndContainerViaEvaluableNodeID(Entity *from_entity, + EvaluableNode *id_node_1, EvaluableNode *id_node_2, + StringInternRef *dest_sid_ref) +{ + if(EvaluableNode::IsNull(id_node_1)) + TraverseToEntityReferenceAndContainerViaEvaluableNodeID(from_entity, id_node_2, dest_sid_ref); + if(EvaluableNode::IsNull(id_node_2)) + TraverseToEntityReferenceAndContainerViaEvaluableNodeID(from_entity, id_node_1, dest_sid_ref); - //index of the target entity's container's id; start at cur_index, - //and if there's room, try to work downward to find the id previous to target_entity_id_index - //if there's nothing between, it won't execute, or it will set them back to being the same - size_t target_container_id_index = cur_index; - if(target_entity_id_index > cur_index) + if(dest_sid_ref == nullptr) { - target_container_id_index = target_entity_id_index - 1; - while(target_container_id_index > 0 && EvaluableNode::IsNull(ocn[target_container_id_index - 1])) - target_container_id_index--; + //assume from_entity contains the container + EntityReadReference container_container(from_entity); + + //assume id_node_1 references container + StringInternPool::StringID sid_1 = EvaluableNode::ToStringIDIfExists(id_node_1); + EntityReferenceType container(container_container->GetContainedEntity(sid_1)); + if(container == nullptr) + return std::make_pair(EntityReferenceType(nullptr), EntityReferenceType(nullptr)); + + //assume id_node_2 references entity + StringInternPool::StringID sid_2 = EvaluableNode::ToStringIDIfExists(id_node_2); + return std::make_pair(EntityReferenceType(container->GetContainedEntity(sid_2)), std::move(container)); + } + else + { + //assume from_entity might be the container + StringInternPool::StringID sid_1 = EvaluableNode::ToStringIDIfExists(id_node_1); + EntityReferenceType possible_container(from_entity->GetContainedEntity(sid_1)); + + //if didn't find a valid possible_container, return nothing + if(possible_container == nullptr) + return std::make_pair(EntityReferenceType(nullptr), EntityReferenceType(nullptr)); + + //see if id_node_2 represents an existing entity + StringInternPool::StringID sid_2 = EvaluableNode::ToStringIDWithReference(id_node_2); + EntityReferenceType possible_target_entity(possible_container->GetContainedEntity(sid_2)); + if(possible_target_entity != nullptr) + { + string_intern_pool.DestroyStringReference(sid_2); + return std::make_pair(EntityReferenceType(nullptr), std::move(possible_target_entity)); + } + + dest_sid_ref->SetIDWithReferenceHandoff(sid_2); + return std::make_pair(EntityReferenceType(nullptr), std::move(possible_container)); + } +} + +//Starts at the container specified and traverses the id path specified, finding the relative Entity to from_entity +//returns a reference of the entity specified by the id path followed by a reference to its container +//if id_path does not exist or is invalid then returns nullptr for both +//if id_path specifies the entity in from_entity, then it returns a reference to it +//if dest_sid_ref is not null, it will assume it is a location to store a string id with reference +// that must be managed by caller +template +std::pair +TraverseToEntityReferenceAndContainerViaEvaluableNodeIDPath( + Entity *from_entity, EvaluableNode *id_path, + StringInternRef *dest_sid_ref = nullptr) +{ + //if the destination sid is requested, initialize it + if(dest_sid_ref != nullptr) + *dest_sid_ref = StringInternRef(string_intern_pool.NOT_A_STRING_ID); + + if(from_entity == nullptr) + return std::make_pair(EntityReferenceType(nullptr), EntityReferenceType(nullptr)); + + if(EvaluableNode::IsEmptyNode(id_path) || id_path->GetType() != ENT_LIST) + return TraverseToEntityReferenceAndContainerViaEvaluableNodeID(from_entity, id_path, dest_sid_ref); + + EvaluableNodeIDPathTraverser traverser(id_path, dest_sid_ref != nullptr); + + //if already at the entity, return + if(traverser.IsEntity()) + return TraverseToEntityReferenceAndContainerViaEvaluableNodeID(from_entity, traverser.GetCurId(), dest_sid_ref); + + //if at the container, lock the container and return the entity + if(traverser.IsContainer()) + { + EvaluableNode *node_id_1 = traverser.GetCurId(); + traverser.AdvanceIndex(); + EvaluableNode *node_id_2 = traverser.GetCurId(); + return TraverseToEntityReferenceAndContainerViaEvaluableNodeID(from_entity, node_id_1, node_id_2, dest_sid_ref); } //the entity is deeper than one of the container's entities, so put a read lock on it and traverse @@ -134,57 +268,43 @@ TraverseToExistingEntityReferenceAndContainerViaEvaluableNodeIDPath( //keep track of a reference for the current entity being considered //and a reference of the type that will be used for the target container EntityReadReference relative_entity_container(from_entity); - ContainerEntityReferenceType target_container(nullptr); - for(; cur_index < non_null_size; cur_index++) + //infinite loop, but logic inside will break it out appropriately + while(true) { - EvaluableNode *cn = ocn[cur_index]; - - //null means current entity wherever it is in the traversal - if(EvaluableNode::IsNull(cn)) - continue; - - //get the string id, get a reference if returning it - if(dest_sid_with_reference == nullptr) - sid = EvaluableNode::ToStringIDIfExists(cn); - else - sid = *dest_sid_with_reference = EvaluableNode::ToStringIDWithReference(cn); + EvaluableNode *cur_node_id = traverser.GetCurId(); + StringInternPool::StringID sid = EvaluableNode::ToStringIDIfExists(cur_node_id); + Entity *next_entity = relative_entity_container->GetContainedEntity(sid); + if(next_entity == nullptr) + break; - from_entity = from_entity->GetContainedEntity(sid); + traverser.AdvanceIndex(); - //if entity doesn't exist, exit gracefully, returning the container if already set - if(from_entity == nullptr) - return std::make_pair(EntityReferenceType(nullptr), std::move(target_container)); - - if(cur_index < target_container_id_index) + if(traverser.IsContainer()) { - relative_entity_container = EntityReadReference(from_entity); - } - else if(cur_index == target_container_id_index) - { - //first acquire the container's reference, then free its container's reference - target_container = ContainerEntityReferenceType(from_entity); - relative_entity_container = EntityReadReference(nullptr); - } - else //cur_index == target_entity_id_index - { - return std::make_pair(EntityReferenceType(from_entity), - std::move(target_container)); + EvaluableNode *next_node_id_1 = traverser.GetCurId(); + traverser.AdvanceIndex(); + EvaluableNode *next_node_id_2 = traverser.GetCurId(); + return TraverseToEntityReferenceAndContainerViaEvaluableNodeID(next_entity, next_node_id_1, next_node_id_2, dest_sid_ref); } + + //traverse the id path for the next loop + + relative_entity_container = EntityReadReference(next_entity); } - //shouldn't make it here - return std::make_pair(EntityReferenceType(nullptr), ContainerEntityReferenceType(nullptr)); + //something failed + return std::make_pair(EntityReferenceType(nullptr), EntityReferenceType(nullptr)); } -//like TraverseToExistingEntityReferenceAndContainerViaEvaluableNodeIDPath +//like TraverseToEntityReferenceAndContainerViaEvaluableNodeIDPath //except only returns the entity requested template inline EntityReferenceType TraverseToExistingEntityReferenceViaEvaluableNodeIDPath( Entity *from_entity, EvaluableNode *id_path) { auto [entity, container] - = TraverseToExistingEntityReferenceAndContainerViaEvaluableNodeIDPath( + = TraverseToEntityReferenceAndContainerViaEvaluableNodeIDPath( from_entity, id_path); return std::move(entity); diff --git a/src/Amalgam/interpreter/Interpreter.cpp b/src/Amalgam/interpreter/Interpreter.cpp index 3b201b33..011de77a 100644 --- a/src/Amalgam/interpreter/Interpreter.cpp +++ b/src/Amalgam/interpreter/Interpreter.cpp @@ -694,29 +694,21 @@ bool Interpreter::InterpretNodeIntoBoolValue(EvaluableNode *n, bool value_if_nul return value; } -void Interpreter::InterpretNodeIntoDestinationEntity(EvaluableNode *n, Entity *&destination_entity_parent, StringInternRef &destination_entity_id) +std::pair Interpreter::InterpretNodeIntoDestinationEntity(EvaluableNode *n) { EvaluableNodeReference destination_entity_id_path = InterpretNodeForImmediateUse(n); - //TODO 10975: change to use TraverseToExistingEntityReferenceAndContainerViaEvaluableNodeIDPath - Entity *container = curEntity; - Entity *destination_entity = nullptr; - TraverseToEntityViaEvaluableNodeIDPath(container, destination_entity_id_path, destination_entity_parent, destination_entity_id, destination_entity); - - //if it already exists, then place inside it - if(destination_entity != nullptr) - { - destination_entity_parent = destination_entity; - destination_entity = nullptr; - - destination_entity_id = StringInternRef::EmptyString(); - } - - //if couldn't get the parent, just use the original container - if(destination_entity_parent == nullptr && destination_entity_id == StringInternPool::NOT_A_STRING_ID) - destination_entity_parent = container; + StringInternRef new_entity_id; + auto [entity, entity_container] = TraverseToEntityReferenceAndContainerViaEvaluableNodeIDPath( + curEntity, destination_entity_id_path, &new_entity_id); evaluableNodeManager->FreeNodeTreeIfPossible(destination_entity_id_path); + + //if it already exists, then place inside it + if(entity != nullptr) + return std::make_pair(std::move(entity), StringInternRef()); + else //return the container + return std::make_pair(std::move(entity_container), new_entity_id); } EvaluableNode **Interpreter::TraverseToDestinationFromTraversalPathList(EvaluableNode **source, EvaluableNodeReference &tpl, bool create_destination_if_necessary) diff --git a/src/Amalgam/interpreter/Interpreter.h b/src/Amalgam/interpreter/Interpreter.h index 27f4a7ac..19846b91 100644 --- a/src/Amalgam/interpreter/Interpreter.h +++ b/src/Amalgam/interpreter/Interpreter.h @@ -399,9 +399,9 @@ class Interpreter bool InterpretNodeIntoBoolValue(EvaluableNode *n, bool value_if_null = false); //Calls InterpretNode on n, converts n into a destination for an Entity, relative to curEntity. - // If invalid, destination_entity_parent will be curEntity and new_entity_id will be the empty string - //new_entity_id is an alocated string, and the caller is responsible for freeing it - void InterpretNodeIntoDestinationEntity(EvaluableNode *n, Entity *&destination_entity_parent, StringInternRef &new_entity_id); + // If invalid, returns a nullptr for the EntityWriteReference + //StringInternRef is an alocated string reference, and the caller is responsible for freeing it + std::pair InterpretNodeIntoDestinationEntity(EvaluableNode *n); //traverses source based on traversal path list tpl // If create_destination_if_necessary is set, then it will expand anything in the source as appropriate diff --git a/src/Amalgam/interpreter/InterpreterOpcodesCodeMixing.cpp b/src/Amalgam/interpreter/InterpreterOpcodesCodeMixing.cpp index d2e0c936..243210ce 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesCodeMixing.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesCodeMixing.cpp @@ -344,14 +344,6 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_MUTATE_ENTITY(EvaluableNod if(ocn.size() > 1) mutation_rate = InterpretNodeIntoNumberValue(ocn[1]); - //get destination if applicable - StringInternRef new_entity_id; - Entity *destination_entity_parent = curEntity; - if(ocn.size() > 2) - InterpretNodeIntoDestinationEntity(ocn[2], destination_entity_parent, new_entity_id); - if(destination_entity_parent == nullptr) - return EvaluableNodeReference::Null(); - bool ow_exists = false; CompactHashMap opcode_weights; if(ocn.size() > 3) @@ -382,7 +374,6 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_MUTATE_ENTITY(EvaluableNod } } - //TODO 10975: change this to lock all entities at once //retrieve the entities after other parameters to minimize time in locks // and prevent deadlock if one of the params accessed the entity //get the id of the first source entity @@ -398,6 +389,20 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_MUTATE_ENTITY(EvaluableNod if(!AllowUnlimitedExecutionNodes()) curNumExecutionNodesAllocatedToEntities += new_entity->GetDeepSizeInNodes(); + //clear lock if applicable + source_entity = EntityReadReference(); + + //get destination if applicable + EntityWriteReference destination_entity_parent; + StringInternRef new_entity_id; + if(ocn.size() > 2) + std::tie(destination_entity_parent, new_entity_id) = InterpretNodeIntoDestinationEntity(ocn[2]); + else + destination_entity_parent = EntityWriteReference(curEntity); + + if(destination_entity_parent == nullptr) + return EvaluableNodeReference::Null(); + destination_entity_parent->AddContainedEntityViaReference(new_entity, new_entity_id, writeListeners); if(new_entity_id == StringInternPool::NOT_A_STRING_ID) @@ -481,10 +486,13 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_INTERSECT_ENTITIES(Evaluab return EvaluableNodeReference::Null(); //get destination if applicable + EntityWriteReference destination_entity_parent; StringInternRef new_entity_id; - Entity *destination_entity_parent = curEntity; if(ocn.size() > 2) - InterpretNodeIntoDestinationEntity(ocn[2], destination_entity_parent, new_entity_id); + std::tie(destination_entity_parent, new_entity_id) = InterpretNodeIntoDestinationEntity(ocn[2]); + else + destination_entity_parent = EntityWriteReference(curEntity); + if(destination_entity_parent == nullptr) return EvaluableNodeReference::Null(); @@ -534,10 +542,13 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_UNION_ENTITIES(EvaluableNo return EvaluableNodeReference::Null(); //get destination if applicable + EntityWriteReference destination_entity_parent; StringInternRef new_entity_id; - Entity *destination_entity_parent = curEntity; if(ocn.size() > 2) - InterpretNodeIntoDestinationEntity(ocn[2], destination_entity_parent, new_entity_id); + std::tie(destination_entity_parent, new_entity_id) = InterpretNodeIntoDestinationEntity(ocn[2]); + else + destination_entity_parent = EntityWriteReference(curEntity); + if(destination_entity_parent == nullptr) return EvaluableNodeReference::Null(); @@ -616,14 +627,6 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_MIX_ENTITIES(EvaluableNode if(ocn.size() > 5) fraction_unnamed_entities_to_mix = InterpretNodeIntoNumberValue(ocn[5]); - //get destination if applicable - StringInternRef new_entity_id; - Entity *destination_entity_parent = curEntity; - if(ocn.size() > 6) - InterpretNodeIntoDestinationEntity(ocn[6], destination_entity_parent, new_entity_id); - if(destination_entity_parent == nullptr) - return EvaluableNodeReference::Null(); - //TODO 10975: change this to lock all entities at once //retrieve the entities after other parameters to minimize time in locks // and prevent deadlock if one of the params accessed the entity @@ -647,6 +650,17 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_MIX_ENTITIES(EvaluableNode if(!AllowUnlimitedExecutionNodes()) curNumExecutionNodesAllocatedToEntities += new_entity->GetDeepSizeInNodes(); + //get destination if applicable + EntityWriteReference destination_entity_parent; + StringInternRef new_entity_id; + if(ocn.size() > 6) + std::tie(destination_entity_parent, new_entity_id) = InterpretNodeIntoDestinationEntity(ocn[6]); + else + destination_entity_parent = EntityWriteReference(curEntity); + + if(destination_entity_parent == nullptr) + return EvaluableNodeReference::Null(); + destination_entity_parent->AddContainedEntityViaReference(new_entity, new_entity_id, writeListeners); if(new_entity_id == StringInternPool::NOT_A_STRING_ID) diff --git a/src/Amalgam/interpreter/InterpreterOpcodesEntityControl.cpp b/src/Amalgam/interpreter/InterpreterOpcodesEntityControl.cpp index 3ac8fdf1..b50e074c 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesEntityControl.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesEntityControl.cpp @@ -347,23 +347,27 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CREATE_ENTITIES(EvaluableN root = InterpretNodeForImmediateUse(ocn[i + 1]); //get destination if applicable + EntityWriteReference entity_container; StringInternRef new_entity_id; - Entity *destination_entity_parent = curEntity; if(i + 1 < ocn.size()) { node_stack.PushEvaluableNode(root); - InterpretNodeIntoDestinationEntity(ocn[i], destination_entity_parent, new_entity_id); + std::tie(entity_container, new_entity_id) = InterpretNodeIntoDestinationEntity(ocn[i]); node_stack.PopEvaluableNode(); } + else + { + entity_container = EntityWriteReference(curEntity); + } - if(destination_entity_parent == nullptr) + if(entity_container == nullptr) { new_entity_ids_list->AppendOrderedChildNode(nullptr); continue; } auto new_entity_id_string = string_intern_pool.GetStringFromID(new_entity_id); - std::string rand_state = destination_entity_parent->CreateRandomStreamFromStringAndRand(new_entity_id_string); + std::string rand_state = entity_container->CreateRandomStreamFromStringAndRand(new_entity_id_string); //create new entity Entity *new_entity = new Entity(root, rand_state, EvaluableNodeManager::ENMM_LABEL_ESCAPE_DECREMENT); @@ -372,7 +376,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CREATE_ENTITIES(EvaluableN if(!AllowUnlimitedExecutionNodes()) curNumExecutionNodesAllocatedToEntities += new_entity->GetDeepSizeInNodes(); - destination_entity_parent->AddContainedEntityViaReference(new_entity, new_entity_id, writeListeners); + entity_container->AddContainedEntityViaReference(new_entity, new_entity_id, writeListeners); if(new_entity_id == StringInternPool::NOT_A_STRING_ID) { @@ -381,7 +385,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CREATE_ENTITIES(EvaluableN continue; } - if(destination_entity_parent == curEntity) + if(entity_container == curEntity) new_entity_ids_list->AppendOrderedChildNode(evaluableNodeManager->AllocNode(ENT_STRING, new_entity_id)); else //need an id path new_entity_ids_list->AppendOrderedChildNode(GetTraversalIDPathFromAToB(evaluableNodeManager, curEntity, new_entity)); @@ -402,11 +406,10 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CLONE_ENTITIES(EvaluableNo new_entity_ids_list->ReserveOrderedChildNodes((ocn.size() + 1) / 2); auto node_stack = CreateInterpreterNodeStackStateSaver(new_entity_ids_list); - //TODO 10975: change this to lock all entities at once for(size_t i = 0; i < ocn.size(); i += 2) { //get the id of the source entity - Entity *source_entity = InterpretNodeIntoRelativeSourceEntityReadReference(ocn[i]); + EntityReadReference source_entity = InterpretNodeIntoRelativeSourceEntityReadReference(ocn[i]); if(source_entity == nullptr) { new_entity_ids_list->AppendOrderedChildNode(nullptr); @@ -414,10 +417,11 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CLONE_ENTITIES(EvaluableNo } //get destination if applicable + EntityWriteReference destination_entity_parent; StringInternRef new_entity_id; - Entity *destination_entity_parent = curEntity; if(i + 1 < ocn.size()) - InterpretNodeIntoDestinationEntity(ocn[i + 1], destination_entity_parent, new_entity_id); + std::tie(destination_entity_parent, new_entity_id) = InterpretNodeIntoDestinationEntity(ocn[i + 1]); + if(destination_entity_parent == nullptr) { new_entity_ids_list->AppendOrderedChildNode(nullptr); @@ -427,6 +431,9 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CLONE_ENTITIES(EvaluableNo //create new entity Entity *new_entity = new Entity(source_entity); + //clear previous lock + source_entity = EntityReadReference(); + //accumulate usage if(!AllowUnlimitedExecutionNodes()) curNumExecutionNodesAllocatedToEntities += new_entity->GetDeepSizeInNodes(); @@ -461,15 +468,13 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_MOVE_ENTITIES(EvaluableNod new_entity_ids_list->ReserveOrderedChildNodes((ocn.size() + 1) / 2); auto node_stack = CreateInterpreterNodeStackStateSaver(new_entity_ids_list); - //TODO 10975: change this to lock the entities for(size_t i = 0; i < ocn.size(); i += 2) { //get the id of the source entity auto source_id_node = InterpretNodeForImmediateUse(ocn[i]); - StringInternRef source_entity_id; - Entity *source_entity_parent = nullptr, *source_entity = nullptr; - TraverseToEntityViaEvaluableNodeIDPath(curEntity, source_id_node, source_entity_parent, source_entity_id, source_entity); + auto [source_entity, source_entity_parent] + = TraverseToEntityReferenceAndContainerViaEvaluableNodeIDPath(curEntity, source_id_node); evaluableNodeManager->FreeNodeTreeIfPossible(source_id_node); if(source_entity == nullptr || source_entity_parent == nullptr || source_entity == curEntity) @@ -478,17 +483,6 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_MOVE_ENTITIES(EvaluableNod continue; } - //get destination if applicable - StringInternRef new_entity_id; - Entity *destination_entity_parent = curEntity; - if(i + 1 < ocn.size()) - InterpretNodeIntoDestinationEntity(ocn[i + 1], destination_entity_parent, new_entity_id); - if(destination_entity_parent == nullptr) - { - new_entity_ids_list->AppendOrderedChildNode(nullptr); - continue; - } - //can't move if being executed if(source_entity->IsEntityCurrentlyBeingExecuted()) { @@ -499,6 +493,24 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_MOVE_ENTITIES(EvaluableNod //remove source entity from its parent source_entity_parent->RemoveContainedEntity(source_entity->GetIdStringId(), writeListeners); + //clear lock if applicable + source_entity_parent = EntityWriteReference(); + + //get destination if applicable + EntityWriteReference destination_entity_parent; + StringInternRef new_entity_id; + if(i + 1 < ocn.size()) + std::tie(destination_entity_parent, new_entity_id) = InterpretNodeIntoDestinationEntity(ocn[i + 1]); + else + destination_entity_parent = EntityWriteReference(curEntity); + + if(destination_entity_parent == nullptr) + { + new_entity_ids_list->AppendOrderedChildNode(nullptr); + delete source_entity; + continue; + } + //put it in the destination destination_entity_parent->AddContainedEntityViaReference(source_entity, new_entity_id, writeListeners); @@ -530,8 +542,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_DESTROY_ENTITIES(Evaluable //get the id of the source entity auto id_node = InterpretNodeForImmediateUse(cn); auto [entity, entity_container] - = TraverseToExistingEntityReferenceAndContainerViaEvaluableNodeIDPath(curEntity, id_node); + = TraverseToEntityReferenceAndContainerViaEvaluableNodeIDPath(curEntity, id_node); evaluableNodeManager->FreeNodeTreeIfPossible(id_node); //need a valid entity that isn't itself or currently has execution @@ -611,10 +622,11 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_LOAD_ENTITY_and_LOAD_PERSI return EvaluableNodeReference::Null(); //get destination if applicable + EntityWriteReference destination_entity_parent; StringInternRef new_entity_id; - Entity *destination_entity_parent = curEntity; - if(ocn.size() >= 2) - InterpretNodeIntoDestinationEntity(ocn[1], destination_entity_parent, new_entity_id); + if(ocn.size() > 1) + std::tie(destination_entity_parent, new_entity_id) = InterpretNodeIntoDestinationEntity(ocn[1]); + if(destination_entity_parent == nullptr) return EvaluableNodeReference::Null();