public void Add(int key, LTrieGenerationNode value) { if (key != size) throw new Exception("LTrieAsciiGenerationMap Generation Map error Add Index"); ReGenerateMapUpToIndex = true; if((_d.Length-1) < key) { Array.Resize(ref _d, key*2); } _d[key] = value; //LTrieGenerationNode node = null; //_d.TryGetValue(key, out node); //if (node == null) // _d.Add(key, value); //else // _d[key] = value; //Place is ok! size++; }
/// <summary> /// Will return pointer to the value of the removing kid (if it existed). Otherwise NULL. /// </summary> /// <param name="key"></param> /// <param name="WasRemoved">indicates that value existed if true</param> /// <param name="retrieveDeletedValue">indicates if we should bind deleted value to the result</param> /// <param name="deletedValue">interesting only if WasRemoved = true and retrieveDeletedValue is true</param> /// <returns></returns> public void RemoveKey(ref byte[] key,out bool WasRemoved, bool retrieveDeletedValue, out byte[] deletedValue) { WasRemoved = false; deletedValue = null; //if (key == null || key.Length == 0) // return; if (key == null) return; if (key.Length > UInt16.MaxValue) throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.KEY_IS_TOO_LONG); LTrieGenerationNode gn = null; if (_generationMap.Count() == 0) { //Loading it from Link TO ZERO Pointer gn = new LTrieGenerationNode(this); gn.Pointer = this.LinkToZeroNode; //gn.Value=0; - default _generationMap.Add(0, gn); gn.ReadSelf(false, null); } LTrieSetupKidResult res = new LTrieSetupKidResult(); //byte[] key1 = null; //we need it as null //byte[] val1 = null; //len can be expanded inside of the algorithm maximum by one int len = key.Length; bool cleanCheck = true; bool iterateFurther = false; /*SPECIAL CASE key=byte[0]*/ if (key.Length == 0) { _generationMap[0].RemoveKid((byte)0, true, ref key, out WasRemoved, retrieveDeletedValue, out deletedValue); return; } /***************************/ for (int i = 0; i < len; i++) { //Getting kid from actual generation map if (cleanCheck && i != 0 && _generationMap.ContainsKey(i) && _generationMap[i].Value != key[i - 1]) { cleanCheck = false; //In case if i>0, it's not the first element and we have to compare if there are generation mapsstarting from this point. //If generationMap[i] exists and it's value not that what we want, we have to clean full in memory generation map as //Save_node... up to i Save_GM_nodes_Starting_From(i); //Remove Gen Map starting from... i _generationMap.RemoveBiggerOrEqualThenKey(i); } if (!_generationMap.ContainsKey(i)) { //All ok, for the first generation node //We read or create generation map //And add it to the _generationMap gn = new LTrieGenerationNode(this); gn.Value = key[i - 1]; gn.Pointer = _generationMap[i - 1].KidsInNode.GetPointerToTheKid(key[i - 1]); //gn.Pointer = _generationMap[i - 1].GetKidPointer(key[i - 1]); _generationMap.Add(i, gn); if (gn.Pointer != null) gn.ReadSelf(false, null); else gn.Pointer = new byte[DefaultPointerLen]; //!!!!!!!!!!!!! Check if it'S really necessary or we can leave it as null } //Trying to remove as a result we receive information if we should iterate further iterateFurther = _generationMap[i].RemoveKid((i == key.Length) ? (byte)0 : key[i], (i == key.Length), ref key, out WasRemoved, retrieveDeletedValue, out deletedValue); if (!iterateFurther) { break; } else { if (i == (key.Length - 1)) len++; } } }
/// <summary> /// /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="WasUpdated">true means that value existed and was updated</param> /// <param name="dontUpdateIfExists">When true - if value exists, we dont update it. If WasUpdated = true then we value exists, if false - we have inserted new one</param> /// <returns></returns> public byte[] AddKey(ref byte[] key, ref byte[] value, out bool WasUpdated, bool dontUpdateIfExists) { //indicates that key we insert, already existed in the system and was updated WasUpdated = false; //if (key == null || key.Length == 0) // return; if (key == null) return null; if (key.Length > UInt16.MaxValue) throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.KEY_IS_TOO_LONG); LTrieGenerationNode gn = null; if (_generationMap.Count() == 0) { //Loading it from Link TO ZERO Pointer gn = new LTrieGenerationNode(this); gn.Pointer = this.LinkToZeroNode; //gn.Value=0; - default _generationMap.Add(0, gn); gn.ReadSelf(false, null); } LTrieSetupKidResult res = new LTrieSetupKidResult(); byte[] key1 = null; //we need it as null byte[] val1 = null; //len can be expanded inside of the algorithm maximum by one int len = key.Length; bool cleanCheck = true; /*Special case key is empty byte[0] */ if (key.Length == 0) { //Saving byte[0] key res = _generationMap[0].SetupKidWithValue((byte)0, true, ref key, ref value, false, out WasUpdated, dontUpdateIfExists); return res.ValueLink; } /**/ for (int i = 0; i < len; i++) { //Getting kid from actual generation map if (cleanCheck && i != 0 && _generationMap.ContainsKey(i) && _generationMap[i].Value != key[i - 1]) { cleanCheck = false; //In case if i>0, it's not the first element and we have to compare if there are generation mapsstarting from this point. //If generationMap[i] exists and it's value not that what we want, we have to clean full in memory generation map as //Save_node... up to i Save_GM_nodes_Starting_From(i); //Remove Gen Map starting from... i _generationMap.RemoveBiggerOrEqualThenKey(i); } if (!_generationMap.ContainsKey(i)) { //All ok, for the first generation node //We read or create generation map //And add it to the _generationMap gn = new LTrieGenerationNode(this); gn.Value = key[i - 1]; gn.Pointer = _generationMap[i - 1].KidsInNode.GetPointerToTheKid(key[i - 1]); //gn.Pointer = _generationMap[i - 1].GetKidPointer(key[i - 1]); _generationMap.Add(i, gn); if (gn.Pointer != null) gn.ReadSelf(false, null); else gn.Pointer = new byte[DefaultPointerLen]; //!!!!!!!!!!!!! Check if it'S really necessary or we can leave it as null } //Generation Node in this trie can have link to kids [0-255] and link to the value. //If Kids>0 && Value Link is not Default Empty Pointer, then this value-link refers to the end of the sentence. //If Kids==0 && Value Link is not Default Empty Pointer, then this value-link refers to the sentence which can go after this last character, so also to the value. //Dual behaviour. if (res.KeyOldKid != null) { //It means that on this stage probably we have to setup both kids //We can check Length of both keys and their current condition //key1 = res.KeyOldKid; //we need it as null val1 = res.ValPtrOldKid; if ((res.KeyOldKid.Length - 1) < i) { _generationMap[i].SetupKidWithValue((byte)0, true, ref key1, ref val1, true, out WasUpdated, dontUpdateIfExists); } else { _generationMap[i].SetupKidWithValue(res.KeyOldKid[i], false, ref key1, ref val1, true, out WasUpdated, dontUpdateIfExists); } //Cleaning up KeyOldKid - probably not necessary, de bene esse (just in case) res.KeyOldKid = null; } //One more only then we setup value, otherwise we bind to the kid, Check Docs in fileDb LtrieSpreadExample1.jpg res = _generationMap[i].SetupKidWithValue(((i == key.Length) ? (byte)0 : key[i]), (i == key.Length), ref key, ref value, false, out WasUpdated, dontUpdateIfExists); if (!res.IterateFurther) { //After setting up value, we can just exit return res.ValueLink; } else { //we don't need value on this phase, we can go on further //Expanding iteration cycle by one and, on the next iteration cycle we should go out from the loop. if (i == (key.Length - 1)) len++; } } //Should not happen as null, we have to return link to the full value return null; } /// <summary> /// Returns link to the full value together with the key /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="startIndex"></param> /// <param name="WasUpdated">indicates that key we insert, already existed in the system and was updated</param> /// <returns></returns> public byte[] AddKeyPartially(ref byte[] key, ref byte[] value, uint startIndex, out long valueStartPtr,out bool WasUpdated) { WasUpdated = false; if (key == null) { valueStartPtr = -1; return null; } if (key.Length > UInt16.MaxValue) throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.KEY_IS_TOO_LONG); if (value == null) throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.PARTIAL_VALUE_CANT_BE_NULL); LTrieGenerationNode gn = null; if (_generationMap.Count() == 0) { //Loading it from Link TO ZERO Pointer gn = new LTrieGenerationNode(this); gn.Pointer = this.LinkToZeroNode; //gn.Value=0; - default _generationMap.Add(0, gn); gn.ReadSelf(false, null); } LTrieSetupKidResult res = new LTrieSetupKidResult(); byte[] key1 = null; //we need it as null byte[] val1 = null; //len can be expanded inside of the algorithm maximum by one int len = key.Length; bool cleanCheck = true; /*Special case key is empty byte[0] */ if (key.Length == 0) { //Saving byte[0] key res = _generationMap[0].SetupKidWithValuePartially((byte)0, true, ref key, ref value, false, startIndex, out valueStartPtr, out WasUpdated); return res.ValueLink; } /**/ for (int i = 0; i < len; i++) { //Getting kid from actual generation map if (cleanCheck && i != 0 && _generationMap.ContainsKey(i) && _generationMap[i].Value != key[i - 1]) { cleanCheck = false; //In case if i>0, it's not the first element and we have to compare if there are generation mapsstarting from this point. //If generationMap[i] exists and it's value not that what we want, we have to clean full in memory generation map as //Save_node... up to i Save_GM_nodes_Starting_From(i); //Remove Gen Map starting from... i _generationMap.RemoveBiggerOrEqualThenKey(i); } if (!_generationMap.ContainsKey(i)) { //All ok, for the first generation node //We read or create generation map //And add it to the _generationMap gn = new LTrieGenerationNode(this); gn.Value = key[i - 1]; gn.Pointer = _generationMap[i - 1].KidsInNode.GetPointerToTheKid(key[i - 1]); //gn.Pointer = _generationMap[i - 1].GetKidPointer(key[i - 1]); _generationMap.Add(i, gn); if (gn.Pointer != null) gn.ReadSelf(false, null); else gn.Pointer = new byte[DefaultPointerLen]; //!!!!!!!!!!!!! Check if it'S really necessary or we can leave it as null } //Generation Node in this trie can have link to kids [0-255] and link to the value. //If Kids>0 && Value Link is not Default Empty Pointer, then this value-link refers to the end of the sentence. //If Kids==0 && Value Link is not Default Empty Pointer, then this value-link refers to the sentence which can go after this last character, so also to the value. //Dual behaviour. if (res.KeyOldKid != null) { //It means that on this stage probably we have to setup both kids //We can check Length of both keys and their current condition //key1 = res.KeyOldKid; //we need it as null val1 = res.ValPtrOldKid; if ((res.KeyOldKid.Length - 1) < i) { _generationMap[i].SetupKidWithValuePartially((byte)0, true, ref key1, ref val1, true, startIndex, out valueStartPtr, out WasUpdated); } else { _generationMap[i].SetupKidWithValuePartially(res.KeyOldKid[i], false, ref key1, ref val1, true, startIndex, out valueStartPtr, out WasUpdated); } //Cleaning up KeyOldKid - probably not necessary, de bene esse (just in case) res.KeyOldKid = null; } //One more only then we setup value, otherwise we bind to the kid, Check Docs in fileDb LtrieSpreadExample1.jpg res = _generationMap[i].SetupKidWithValuePartially(((i == key.Length) ? (byte)0 : key[i]), (i == key.Length), ref key, ref value, false, startIndex, out valueStartPtr, out WasUpdated); if (!res.IterateFurther) { //After setting up value, we can just exit return res.ValueLink; } else { //we don't need value on this phase, we can go on further //Expanding iteration cycle by one and, on the next iteration cycle we should go out from the loop. if (i == (key.Length - 1)) len++; } } //Should not happen as null, we have to return link to the full value valueStartPtr = -1; return null; } /// <summary> /// Takes value fresh no committed value row.GetFullValue(false); /// </summary> /// <param name="oldKey"></param> /// <param name="newKey"></param> /// <returns></returns> public bool ChangeKey(ref byte[] oldKey, ref byte[] newKey) { byte[] refToInsertedValue = null; return this.ChangeKey(ref oldKey, ref newKey, out refToInsertedValue); } /// <summary> /// Takes value fresh no committed value row.GetFullValue(false); /// </summary> /// <param name="oldKey"></param> /// <param name="newKey"></param> /// <param name="refToInsertedValue">returns ptr in the file to the new key</param> /// <returns></returns> public bool ChangeKey(ref byte[] oldKey, ref byte[] newKey, out byte[] refToInsertedValue) { //The best way read old, remove, and create new, with holding of transactions, //just changing pointers to the value will give nothing, because in the value also the full key is written, so we will need //to make new value refToInsertedValue = null; var row = this.GetKey(oldKey,false); if (row.Exists) { byte[] oldKeyValue = row.GetFullValue(false); bool WasRemoved = false; byte[] deletedValue = null; this.RemoveKey(ref oldKey, out WasRemoved, false, out deletedValue); bool WasUpdated = false; refToInsertedValue = this.AddKey(ref newKey, ref oldKeyValue,out WasUpdated,false); refToInsertedValue = refToInsertedValue.EnlargeByteArray_BigEndian(8); return true; } return false; } public void Commit() { try { this.Save_GM_nodes_Starting_From(0); byte[] oldRoot = me; me = this.SerializeRootNode(); //Synchronized inside //DBreeze.Diagnostic.SpeedStatistic.StartCounter("Commit"); //this.Tree.Cache.Commit(this.EmptyPointer, ref me, ref oldRoot); this.Tree.Cache.Commit(ref me, ref oldRoot); //DBreeze.Diagnostic.SpeedStatistic.StopCounter("Commit"); } catch (Exception ex) { ////rollbak will be done on the level of the tree //////////////////////////////////////// throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.COMMIT_FAILED, this.Tree.TableName, ex); } } /// <summary> /// /// </summary> /// <param name="key"></param> /// <returns></returns> public LTrieRow GetKey(byte[] key, bool useCache) { //Later change TreeKVP (for RootNode Interface or smth like this) and make it unversal, this one must return value LTrieRow kv = new LTrieRow(this); kv.Key = key; //if (key == null || key.Length == 0) // return kv; if (key == null) return kv; LTrieGenerationNode gn = null; if (_generationMap.Count() == 0) { //Loading it from Link TO ZERO Pointer gn = new LTrieGenerationNode(this); gn.Pointer = this.LinkToZeroNode; //gn.Value=0; - default _generationMap.Add(0, gn); gn.ReadSelf(useCache, _generationMap.GenerateMapNodesValuesUpToIndex(0)); //gn.ReadSelf(); } bool cleanCheck = true; LTrieKid kidDef = null; int p = 0; int len = key.Length; /*SPECIAL CASE key = byte[0]*/ if (key.Length == 0) { kidDef = _generationMap[0].GetKidAsValue(true, 1); if (kidDef.Exists) { kv.LinkToValue = kidDef.Ptr; } return kv; } /****************************/ for (int i = 0; i < len; i++) { //Getting kid from actual generation map if (cleanCheck && i != 0 && _generationMap.ContainsKey(i) && _generationMap[i].Value != key[i - 1]) { cleanCheck = false; _generationMap.RemoveBiggerOrEqualThenKey(i); } if (!_generationMap.ContainsKey(i)) { gn = new LTrieGenerationNode(this); gn.Value = key[i - 1]; gn.Pointer = _generationMap[i - 1].KidsInNode.GetPointerToTheKid(key[i - 1]); //FIND A SOLUTION FOR THIS NULL or EMPTY POINTER //if (gn.Pointer == null || this._IfPointerIsEmpty(gn.Pointer)) // return kv; if (gn.Pointer == null || gn.Pointer._IfPointerIsEmpty(this.DefaultPointerLen)) return kv; _generationMap.Add(i, gn); gn.ReadSelf(useCache, _generationMap.GenerateMapNodesValuesUpToIndex(i)); //gn.ReadSelf(); } //Also if last element then supply 256 to get value not the link to value (if no value exit) //If kid is a link to next node we iterate further, if link on the value, we retrieve full key and value as link for TreeKVP stoping iteration //If link is empty (no kid) we return empty if (i >= key.Length) p = i - 1; else p = i; kidDef = _generationMap[i].GetKidAsValue((i >= (key.Length)), key[p]); if (kidDef.Exists) { if (kidDef.ValueKid) { kv.LinkToValue = kidDef.Ptr; return kv; } if (!kidDef.LinkToNode) { //byte[] storedKey = _generationMap[i].ReadKidKeyFromValPtr(kidDef.Ptr); long valueStartPtr = 0; uint valueLength = 0; byte[] xValue = null; byte[] storedKey = null; if (!this.Tree.ValuesLazyLoadingIsOn) { this.Tree.Cache.ReadKeyValue(useCache, kidDef.Ptr, out valueStartPtr, out valueLength, out storedKey, out xValue); } else { storedKey = this.Tree.Cache.ReadKey(useCache, kidDef.Ptr); } // byte[] storedKey = this.Tree.Cache.ReadKey(useCache, kidDef.Ptr); if (key.Length != storedKey.Length || !key._ByteArrayEquals(storedKey)) return kv; if (!this.Tree.ValuesLazyLoadingIsOn) { kv.ValueStartPointer = valueStartPtr; kv.ValueFullLength = valueLength; kv.Value = xValue; kv.ValueIsReadOut = true; } kv.LinkToValue = kidDef.Ptr; return kv; } if (i == key.Length - 1) len++; //iterating further } else return kv; } return kv; }
/// <summary> /// Check TransactionCommit in case of RemoveAll with file Recreation. /// Note if some other threads are reading parallel data, exception will be thrown in their transaction. /// It's correct. /// </summary> /// <param name="withFileRecreation"></param> public void RemoveAll(bool withFileRecreation) { if (!withFileRecreation) { LTrieGenerationNode gn = null; _generationMap.Clear(); if (_generationMap.Count() == 0) { //Loading it from Link TO ZERO Pointer gn = new LTrieGenerationNode(this); gn.Pointer = this.LinkToZeroNode; //gn.Value=0; - default _generationMap.Add(0, gn); gn.ReadSelf(false, null); } _generationMap[0].RemoveAllKids(); } else { try { this.Tree.Cache.RecreateDB(); //Important, Better to re-read all generation nodes for safety reasons, de bene esse this._generationMap.Clear(); //And re-Read RootNode ReadRootNode(); } catch (Exception ex) { //////////////////// MADE THAT Table is not Opearable on the upper level, throw DBreezeException.Throw(DBreezeException.eDBreezeExceptions.RECREATE_TABLE_FAILED, this.Tree.TableName, ex); } } }