public void MoveEntityFromDictionaryAndEngines(EGID fromEntityGid, EGID?toEntityID, ITypeSafeDictionary toGroup, Dictionary <Type, FasterList <IEngine> > engines, ref PlatformProfiler profiler) { var valueIndex = GetValueIndex(fromEntityGid.entityID); if (toGroup != null) { RemoveEntityViewFromEngines(engines, ref _values[valueIndex], fromEntityGid.groupID, ref profiler); var toGroupCasted = toGroup as TypeSafeDictionary <TValue>; ref var entity = ref _values[valueIndex]; var previousGroup = fromEntityGid.groupID; /// /// NOTE I WOULD EVENTUALLY NEED TO REUSE THE REAL ID OF THE REMOVING ELEMENT /// SO THAT I CAN DECREASE THE GLOBAL GROUP COUNT /// // entity.ID = EGID.UPDATE_REAL_ID_AND_GROUP(entity.ID, toEntityID.groupID, entityCount); if (HasEgid) { var needEgid = (INeedEGID)entity; needEgid.ID = toEntityID.Value; entity = (TValue)needEgid; } var index = toGroupCasted.Add(fromEntityGid.entityID, ref entity); AddEntityViewToEngines(engines, ref toGroupCasted._values[index], previousGroup, ref profiler); }
public void BuildEntityViewAndAddToList(ref ITypeSafeDictionary dictionary, EGID entityID, object[] implementors) { if (dictionary == null) { dictionary = new TypeSafeDictionary <T>(); } var castedDic = dictionary as TypeSafeDictionary <T>; if (needsReflection == true) { DBC.ECS.Check.Require(implementors != null, "Implementors not found while building an EntityView"); T lentityView; EntityView <T> .BuildEntityView(entityID, out lentityView); this.FillEntityView(ref lentityView , entityViewBlazingFastReflection , implementors , DESCRIPTOR_NAME); castedDic.Add(entityID.entityID, ref lentityView); } else { _initializer.ID = entityID; castedDic.Add(entityID.entityID, _initializer); } }
/// <summary> /// Add entities from external typeSafeDictionary /// </summary> /// <param name="entitiesToSubmit"></param> /// <param name="groupId"></param> /// <exception cref="TypeSafeDictionaryException"></exception> public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId) { var typeSafeDictionary = entitiesToSubmit as FastTypeSafeDictionary <TValue>; foreach (var tuple in typeSafeDictionary) { try { if (_hasEgid) { SetEGIDWithoutBoxing <TValue> .SetIDWithoutBoxing(ref typeSafeDictionary.unsafeValues[tuple.Key], new EGID(tuple.Key, groupId)); } _implementation.Add(tuple.Value); } catch (Exception e) { throw new TypeSafeDictionaryException("trying to add an EntityComponent with the same ID more than once Entity: ". FastConcat(typeof(TValue).ToString()).FastConcat(", group "). FastConcat(groupId).FastConcat(", id ").FastConcat(tuple.Key), e); } } }
internal static EGIDMapper <T> ToEGIDMapper <T>(this ITypeSafeDictionary <T> dic, ExclusiveGroupStruct groupStructId) where T : struct, IBaseEntityComponent { var mapper = new EGIDMapper <T>(groupStructId, dic); return(mapper); }
void MoveEntity(EGID fromEntityGID, int toGroupID, Dictionary <Type, ITypeSafeDictionary> toGroup, Type entityType) { var fromGroup = _groupEntityDB[fromEntityGID.groupID]; var fromTypeSafeDictionary = fromGroup[entityType]; ITypeSafeDictionary safeDictionary = null; if (toGroup != null) { if (toGroup.TryGetValue(entityType, out safeDictionary) == false) { safeDictionary = fromTypeSafeDictionary.Create(); toGroup.Add(entityType, safeDictionary); _groupedGroups[entityType] = new FasterDictionary <int, ITypeSafeDictionary>(); } _groupedGroups[entityType][toGroupID] = safeDictionary; } fromTypeSafeDictionary.MoveEntityFromDictionaryAndEngines(fromEntityGID, toGroupID, safeDictionary, _entityEngines); if (fromTypeSafeDictionary.Count == 0) //clean up { _groupedGroups[entityType].Remove(fromEntityGID.groupID); //it's probably better to not remove this, but the dictionary should be trimmed? //fromGroup.Remove(entityType); fromTypeSafeDictionary.Trim(); } //it doesn't eliminate the fromGroup itself on purpose }
public void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID entityID, object[] implementors) { if (dictionary == null) { dictionary = new TypeSafeDictionary <T>(); } var castedDic = dictionary as TypeSafeDictionary <T>; if (NEEDS_REFLECTION == true) { DBC.ECS.Check.Require(implementors != null, "Implementors not found while building an EntityView"); DBC.ECS.Check.Require(castedDic.ContainsKey(entityID.entityID) == false, "building an entity with already used entity id! id".FastConcat(entityID).FastConcat(" ", ENTITY_VIEW_NAME)); T entityView; EntityView <T> .BuildEntityView(entityID, out entityView); this.FillEntityView(ref entityView , entityViewBlazingFastReflection , implementors); castedDic.Add(entityID.entityID, ref entityView); } else { _initializer.ID = entityID; castedDic.Add(entityID.entityID, _initializer); } }
public void AddEntitiesFromDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId) { var typeSafeDictionary = entitiesToSubmit as TypeSafeDictionary <TValue>; foreach (var tuple in typeSafeDictionary) { try { if (HasEgid) { var needEgid = (INeedEGID)tuple.Value; needEgid.ID = new EGID(tuple.Key, groupId); Add(tuple.Key, (TValue)needEgid); } else { Add(tuple.Key, ref tuple.Value); } } catch (Exception e) { throw new TypeSafeDictionaryException( "trying to add an EntityView with the same ID more than once Entity: " .FastConcat(typeof(TValue).ToString()).FastConcat("id ").FastConcat(tuple.Key), e); } } }
public bool MoveNext() { //attention, the while is necessary to skip empty groups while (_db.MoveNext() == true) { var group = _db.Current; if (group.key.IsEnabled() == false) { continue; } ITypeSafeDictionary <T1> typeSafeDictionary = @group.value as ITypeSafeDictionary <T1>; if (typeSafeDictionary.count == 0) { continue; } _array.collection = new EntityCollection <T1>(typeSafeDictionary.GetValues(out var count), typeSafeDictionary.entityIDs, count); _array.@group = group.key; return(true); } return(false); }
public static void MoveEntityView(EGID entityID, int toGroupID, ITypeSafeDictionary fromSafeDic, ITypeSafeDictionary toSafeDic) { var fromCastedDic = fromSafeDic as TypeSafeDictionary <T>; var toCastedDic = toSafeDic as TypeSafeDictionary <T>; var entity = fromCastedDic[entityID.entityID]; fromCastedDic.Remove(entityID.entityID); entity.ID = new EGID(entityID.entityID, toGroupID); toCastedDic.Add(entityID.entityID, entity); }
public void AddEntitiesToEngines( Dictionary <Type, FasterList <IEngine> > entityViewEnginesDB, ITypeSafeDictionary realDic, ref PlatformProfiler profiler) { foreach (var value in this) { var typeSafeDictionary = realDic as TypeSafeDictionary <TValue>; AddEntityViewToEngines(entityViewEnginesDB, ref typeSafeDictionary.GetDirectValue(value.Key), null, ref profiler); } }
static void BuildEntity (EGID entityID, FasterDictionary <RefWrapperType, ITypeSafeDictionary> group , IComponentBuilder componentBuilder, IEnumerable <object> implementors) { var entityComponentType = componentBuilder.GetEntityComponentType(); ITypeSafeDictionary safeDictionary = group.GetOrAdd(new RefWrapperType(entityComponentType) , (ref IComponentBuilder cb) => cb.CreateDictionary(1) , ref componentBuilder); // if the safeDictionary hasn't been created yet, it will be created inside this method. componentBuilder.BuildEntityAndAddToList(safeDictionary, entityID, implementors); }
public void Serialize(uint entityID, ITypeSafeDictionary dictionary, ISerializationData serializationData , int serializationType) { IComponentSerializer <T> componentSerializer = _serializers[serializationType]; var safeDictionary = (ITypeSafeDictionary <T>)dictionary; if (safeDictionary.TryFindIndex(entityID, out uint index) == false) { throw new ECSException("Entity Serialization failed"); } ref T val = ref safeDictionary.GetDirectValueByRef(index);
public static ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, int size) { if (dictionary == null) { dictionary = new TypeSafeDictionary <T>(size); } else { dictionary.AddCapacity(size); } return(dictionary); }
void MoveEntityView(EGID entityGID, EGID?toEntityGID, Dictionary <Type, ITypeSafeDictionary> toGroup, ref Dictionary <Type, ITypeSafeDictionary> fromGroup, Type entityViewType, PlatformProfiler profiler) { if (fromGroup.TryGetValue(entityViewType, out var fromTypeSafeDictionary) == false) { throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID) .FastConcat(" group: ").FastConcat(entityGID.groupID)); } ITypeSafeDictionary toEntitiesDictionary = null; //in case we want to move to a new group, otherwise is just a remove if (toEntityGID != null) { if (toGroup.TryGetValue(entityViewType, out toEntitiesDictionary) == false) { toEntitiesDictionary = fromTypeSafeDictionary.Create(); toGroup.Add(entityViewType, toEntitiesDictionary); } if (_groupsPerEntity.TryGetValue(entityViewType, out var groupedGroup) == false) { groupedGroup = _groupsPerEntity[entityViewType] = new FasterDictionary <uint, ITypeSafeDictionary>(); } groupedGroup[toEntityGID.Value.groupID] = toEntitiesDictionary; } if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) { throw new EntityNotFoundException(entityGID, entityViewType); } fromTypeSafeDictionary.MoveEntityFromDictionaryAndEngines(entityGID, toEntityGID, toEntitiesDictionary, toEntityGID == null ? _reactiveEnginesAddRemove : _reactiveEnginesSwap, ref profiler); if (fromTypeSafeDictionary.Count == 0) //clean up { _groupsPerEntity[entityViewType].Remove(entityGID.groupID); //I don't remove the group if empty on purpose, in case it needs to be reused however I trim it to save //memory fromTypeSafeDictionary.Trim(); } }
public void AddEntityToDictionary(EGID fromEntityGid, EGID toEntityID, ITypeSafeDictionary toGroup) { var valueIndex = _implementation.GetIndex(fromEntityGid.entityID); if (toGroup != null) { var toGroupCasted = toGroup as ITypeSafeDictionary <TValue>; ref var entity = ref _implementation.unsafeValues[(int)valueIndex]; if (_hasEgid) { SetEGIDWithoutBoxing <TValue> .SetIDWithoutBoxing(ref entity, toEntityID); } toGroupCasted.Add(fromEntityGid.entityID, entity); }
public void FillWithIndexedEntities(ITypeSafeDictionary entities) { int count; var buffer = (entities as TypeSafeDictionary <TValue>).GetValuesArray(out count); try { for (var i = 0; i < count; i++) { Add(buffer[i].ID.entityID, ref buffer[i]); } } catch (Exception e) { throw new TypeSafeDictionaryException(e); } }
void MoveEntityView(EGID entityGID, int toGroupID, Dictionary <Type, ITypeSafeDictionary> toGroup, Dictionary <Type, ITypeSafeDictionary> fromGroup, Type entityType) { ITypeSafeDictionary fromTypeSafeDictionary; if (fromGroup.TryGetValue(entityType, out fromTypeSafeDictionary) == false) { throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID).FastConcat(" group: ").FastConcat(entityGID.groupID)); } ITypeSafeDictionary dictionaryOfEntities = null; //in case we want to move to a new group, otherwise is just a remove if (toGroup != null) { if (toGroup.TryGetValue(entityType, out dictionaryOfEntities) == false) { dictionaryOfEntities = fromTypeSafeDictionary.Create(); toGroup.Add(entityType, dictionaryOfEntities); } FasterDictionary <int, ITypeSafeDictionary> groupedGroup; if (_groupedGroups.TryGetValue(entityType, out groupedGroup) == false) { groupedGroup = _groupedGroups[entityType] = new FasterDictionary <int, ITypeSafeDictionary>(); } groupedGroup[toGroupID] = dictionaryOfEntities; } if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) { throw new ECSException("entity not found eid: ".FastConcat(entityGID.entityID).FastConcat(" group: ").FastConcat(entityGID.groupID)); } fromTypeSafeDictionary.MoveEntityFromDictionaryAndEngines(entityGID, toGroupID, dictionaryOfEntities, _entityEngines); if (fromTypeSafeDictionary.Count == 0) //clean up { _groupedGroups[entityType].Remove(entityGID.groupID); //I don't remove the group if empty on purpose, in case it needs to be reused //however I trim it to save memory fromTypeSafeDictionary.Trim(); } }
public bool MoveNext() { //attention, the while is necessary to skip empty groups while (_db.MoveNext() == true) { FasterDictionary<uint, ITypeSafeDictionary>.KeyValuePairFast group = _db.Current; ITypeSafeDictionary<T1> typeSafeDictionary = @group.Value as ITypeSafeDictionary<T1>; if (typeSafeDictionary.count == 0) continue; _array.collection = new EntityCollection<T1>(typeSafeDictionary.GetValues(out var count), count); _array.@group = new ExclusiveGroupStruct(group.Key); return true; } return false; }
public void SwapEntitiesInGroup (ExclusiveBuildGroup fromGroupID, ExclusiveBuildGroup toGroupID, [CallerMemberName] string caller = null) { if (_enginesRoot.Target._groupEntityComponentsDB.TryGetValue( fromGroupID.group , out FasterDictionary <RefWrapperType, ITypeSafeDictionary> entitiesInGroupPerType) == true) { #if DEBUG && !PROFILE_SVELTO ITypeSafeDictionary dictionary = entitiesInGroupPerType.unsafeValues[0]; dictionary.KeysEvaluator((key) => { _enginesRoot.Target.CheckRemoveEntityID(new EGID(key, fromGroupID), null, caller); _enginesRoot.Target.CheckAddEntityID(new EGID(key, toGroupID), null, caller); }); #endif _enginesRoot.Target.QueueSwapGroupOperation(fromGroupID, toGroupID, caller); } }
public void FillWithIndexedEntityViews(ITypeSafeDictionary entityViews) { int count; var buffer = (entityViews as TypeSafeDictionary <TValue>).GetFasterValuesBuffer(out count); try { for (var i = 0; i < count; i++) { var entityView = buffer[i]; Add(entityView.ID.entityID, entityView); } } catch (Exception e) { throw new TypeSafeDictionaryException(e); } }
public void FillWithIndexedEntities(ITypeSafeDictionary entitiesToSubmit) { var buffer = (entitiesToSubmit as TypeSafeDictionary <TValue>).GetValuesArray(out var count); for (var i = 0; i < count; i++) { try { // buffer[i].ID = EGID.UPDATE_REAL_ID(buffer[i].ID, entityCount); Add(buffer[i].ID.entityID, ref buffer[i]); } catch (Exception e) { throw new TypeSafeDictionaryException( "trying to add an EntityView with the same ID more than once Entity: " .FastConcat(typeof(TValue).ToString()).FastConcat("id ").FastConcat(buffer[i].ID.entityID), e); } } }
void CopyEntityToDictionary(EGID entityGID, EGID toEntityGID, FasterDictionary <RefWrapper <Type>, ITypeSafeDictionary> fromGroup, FasterDictionary <RefWrapper <Type>, ITypeSafeDictionary> toGroup, Type entityViewType) { var wrapper = new RefWrapper <Type>(entityViewType); ITypeSafeDictionary fromTypeSafeDictionary = GetTypeSafeDictionary(entityGID.groupID, fromGroup, wrapper); #if DEBUG && !PROFILER if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) { throw new EntityNotFoundException(entityGID, entityViewType); } #endif ITypeSafeDictionary toEntitiesDictionary = GetOrCreateTypeSafeDictionary(toEntityGID.groupID, toGroup, wrapper, fromTypeSafeDictionary); fromTypeSafeDictionary.AddEntityToDictionary(entityGID, toEntityGID, toEntitiesDictionary); }
public void FillWithIndexedEntities(ITypeSafeDictionary entities) { int count; var buffer = (entities as TypeSafeDictionary <TValue>).GetValuesArray(out count); for (var i = 0; i < count; i++) { int idEntityId = 0; try { idEntityId = buffer[i].ID.entityID; Add(idEntityId, ref buffer[i]); } catch (Exception e) { throw new TypeSafeDictionaryException("trying to add an EntityView with the same ID more than once Entity: ". FastConcat(typeof(TValue)).FastConcat("id ").FastConcat(idEntityId), e); } } }
public void CopyTypeSafeDictionary(ITypeSafeDictionary entitiesToSubmit, uint groupId) { var typeSafeDictionary = (entitiesToSubmit as TypeSafeDictionary <TValue>); foreach (var value in typeSafeDictionary) { try { if (HasEgid) { (value.Value as INeedEGID).ID = new EGID(value.Key, groupId); } Add(value.Key, value.Value); } catch (Exception e) { throw new TypeSafeDictionaryException( "trying to add an EntityView with the same ID more than once Entity: " .FastConcat(typeof(TValue).ToString()).FastConcat("id ").FastConcat(value.Key), e); } } }
public bool MoveNext() { //attention, the while is necessary to skip empty groups while (_db.MoveNext() == true) { FasterDictionary<uint, ITypeSafeDictionary>.KeyValuePairFast group = _db.Current; ITypeSafeDictionary<T1> typeSafeDictionary1 = @group.Value as ITypeSafeDictionary<T1>; ITypeSafeDictionary<T2> typeSafeDictionary2 = @group.Value as ITypeSafeDictionary<T2>; DBC.ECS.Check.Require(typeSafeDictionary1.Count != typeSafeDictionary2.Count , "entities count do not match"); if (typeSafeDictionary1.Count == 0) continue; _array = new BT<NB<T1>, NB<T2>>()(new EntityCollection<T1>(typeSafeDictionary1.GetValuesArray(out var count), count) .ToBuffer(); return true; } return false; }
public void MoveEntityFromDictionaryAndEngines(EGID fromEntityGid, int toGroupID, ITypeSafeDictionary toGroup, Dictionary <Type, FasterList <IHandleEntityViewEngineAbstracted> > entityViewEnginesDB) { int count; var fasterValuesBuffer = GetValuesArray(out count); var valueIndex = GetValueIndex(fromEntityGid.entityID); if (entityViewEnginesDB != null) { RemoveEntityViewFromEngines(entityViewEnginesDB, ref fasterValuesBuffer[valueIndex]); } if (toGroup != null) { var toGroupCasted = toGroup as TypeSafeDictionary <TValue>; fasterValuesBuffer[valueIndex].ID = new EGID(fromEntityGid.entityID, toGroupID); toGroupCasted.Add(fromEntityGid.entityID, ref fasterValuesBuffer[valueIndex]); if (entityViewEnginesDB != null) { AddEntityViewToEngines(entityViewEnginesDB, ref toGroupCasted.GetValuesArray(out count)[toGroupCasted.GetValueIndex(fromEntityGid.entityID)]); } } Remove(fromEntityGid.entityID); }
ITypeSafeDictionary IEntityBuilder.Preallocate(ref ITypeSafeDictionary dictionary, int size) { return(Preallocate(ref dictionary, size)); }
internal EGIDMapper(ExclusiveGroupStruct groupStructId, ITypeSafeDictionary <T> dic) : this() { groupID = groupStructId; _map = dic; }
/// <summary> /// Todo: it would be probably better to split even further the logic between submission and callbacks /// Something to do when I will optimize the callbacks /// </summary> /// <param name="profiler"></param> /// <param name="maxNumberOfOperations"></param> IEnumerator SingleSubmission(PlatformProfiler profiler, uint maxNumberOfOperations) { #if UNITY_NATIVE NativeOperationSubmission(profiler); #endif ClearChecks(); bool entitiesAreSubmitted = false; uint numberOfOperations = 0; if (_entitiesOperations.count > 0) { using (profiler.Sample("Remove and Swap operations")) { _transientEntitiesOperations.FastClear(); _entitiesOperations.CopyValuesTo(_transientEntitiesOperations); _entitiesOperations.FastClear(); EntitySubmitOperation[] entitiesOperations = _transientEntitiesOperations.ToArrayFast(out var count); for (var i = 0; i < count; i++) { try { switch (entitiesOperations[i].type) { case EntitySubmitOperationType.Swap: MoveEntityFromAndToEngines(entitiesOperations[i].builders, entitiesOperations[i].fromID, entitiesOperations[i].toID); break; case EntitySubmitOperationType.Remove: MoveEntityFromAndToEngines(entitiesOperations[i].builders, entitiesOperations[i].fromID, null); break; case EntitySubmitOperationType.RemoveGroup: RemoveEntitiesFromGroup( entitiesOperations[i].fromID.groupID, profiler); break; case EntitySubmitOperationType.SwapGroup: SwapEntitiesBetweenGroups(entitiesOperations[i].fromID.groupID, entitiesOperations[i].toID.groupID, profiler); break; } } catch { var str = "Crash while executing Entity Operation " .FastConcat(entitiesOperations[i].type.ToString()); Svelto.Console.LogError(str.FastConcat(" ") #if DEBUG && !PROFILE_SVELTO .FastConcat(entitiesOperations[i].trace.ToString()) #endif ); throw; } ++numberOfOperations; if ((uint)numberOfOperations >= (uint)maxNumberOfOperations) { yield return(null); numberOfOperations = 0; } } } entitiesAreSubmitted = true; } _groupedEntityToAdd.Swap(); if (_groupedEntityToAdd.otherEntitiesCreatedPerGroup.count > 0) { using (profiler.Sample("Add operations")) { try { using (profiler.Sample("Add entities to database")) { //each group is indexed by entity view type. for each type there is a dictionary indexed by entityID foreach (var groupToSubmit in _groupedEntityToAdd.otherEntitiesCreatedPerGroup) { var groupID = groupToSubmit.Key; var groupDB = GetOrCreateGroup(groupID, profiler); //add the entityComponents in the group foreach (var entityComponentsToSubmit in _groupedEntityToAdd.other[groupID]) { var type = entityComponentsToSubmit.Key; var targetTypeSafeDictionary = entityComponentsToSubmit.Value; var wrapper = new RefWrapperType(type); ITypeSafeDictionary dbDic = GetOrCreateTypeSafeDictionary(groupID, groupDB, wrapper, targetTypeSafeDictionary); //Fill the DB with the entity components generate this frame. dbDic.AddEntitiesFromDictionary(targetTypeSafeDictionary, groupID); } } } //then submit everything in the engines, so that the DB is up to date with all the entity components //created by the entity built using (profiler.Sample("Add entities to engines")) { foreach (var groupToSubmit in _groupedEntityToAdd.otherEntitiesCreatedPerGroup) { var groupID = groupToSubmit.Key; var groupDB = _groupEntityComponentsDB[groupID]; //entityComponentsToSubmit is the array of components found in the groupID per component type. //if there are N entities to submit, and M components type to add for each entity, this foreach will run NxM times. foreach (var entityComponentsToSubmit in _groupedEntityToAdd.other[groupID]) { var realDic = groupDB[new RefWrapperType(entityComponentsToSubmit.Key)]; entityComponentsToSubmit.Value.ExecuteEnginesAddOrSwapCallbacks(_reactiveEnginesAddRemove, realDic, null, new ExclusiveGroupStruct(groupID), in profiler); numberOfOperations += entityComponentsToSubmit.Value.count; if (numberOfOperations >= maxNumberOfOperations) { yield return(null); numberOfOperations = 0; } } } } } finally { using (profiler.Sample("clear double buffering")) { //other can be cleared now, but let's avoid deleting the dictionary every time _groupedEntityToAdd.ClearOther(); } } } entitiesAreSubmitted = true; } if (entitiesAreSubmitted) { var enginesCount = _reactiveEnginesSubmission.count; for (int i = 0; i < enginesCount; i++) { _reactiveEnginesSubmission[i].EntitiesSubmitted(); } } }
void IEntityBuilder.MoveEntityView(EGID entityID, int toGroupID, ITypeSafeDictionary fromSafeDic, ITypeSafeDictionary toSafeDic) { MoveEntityView(entityID, toGroupID, fromSafeDic, toSafeDic); }