Esempio n. 1
0
        /// <summary>
        /// Get numeric value
        /// </summary>
        /// <param name="sourse"></param>
        /// <returns></returns>
        private long GetNumericValue(string p)
        {
            long n = -1;

            try
            {
                string ost = "";
                string ist = GetStringValue(p);
                for (int i = 0; i < ist.Length; i++)
                {
                    int    res = 0;
                    string sym = ist.Substring(i, 1);
                    if (int.TryParse(sym, out res))
                    {
                        ost += sym;
                    }
                    else
                    {
                        break;
                    }
                }
                n = VSLib.ConvertStringToLong(ost);
            }
            catch (Exception e1)
            {
                this.Error = e1.Message;
                return(-1);
            }
            return(n);
        }
Esempio n. 2
0
        /// <summary>
        /// Save catalog content
        /// </summary>
        public void Save()
        {
            string ost = sg_ok + DEFS.VSTORAGE_VERSION + "$" + ste + "$" + DEFS.DELIM_NEWLINE;

            for (int i = 0; i < dl.Count; i++)
            {
                ost += "[" + dl[i].Name + "]" + DEFS.DELIM_NEWLINE;

                ost += DEF_ID + "=" + dl[i].Id.ToString() + DEFS.DELIM_NEWLINE;
                ost += DEF_PAGESIZE + "=" + dl[i].page_size_kb.ToString() + "Kb" + DEFS.DELIM_NEWLINE;
                ost += DEF_SIZE + "=" + dl[i].space_size_pg.ToString() + "Pages" + DEFS.DELIM_NEWLINE;
                ost += DEF_EXTENSION + "=" + dl[i].extension_pg.ToString() + "Pages" + DEFS.DELIM_NEWLINE;
                ost += DEF_PARTITIONS + "=" + dl[i].partitions.ToString() + DEFS.DELIM_NEWLINE;

                ost += DEF_TIMESTAMP + "=" + dl[i].creation_timestamp + DEFS.DELIM_NEWLINE;
                ost += DEF_PATH + "=" + dl[i].path + DEFS.DELIM_NEWLINE;
                ost += DEF_INDEXSPACE + "=" + dl[i].indexspace + DEFS.DELIM_NEWLINE;

                // Create signature
                ost += DEF_SIGNATURE + "=" + dl[i].CalculateSignature() + DEFS.DELIM_NEWLINE;
            }

            if (File.Exists(catalog_file_name))
            {
                if (File.Exists(backup_file_name))
                {
                    File.Delete(backup_file_name);
                }
                File.Move(catalog_file_name, backup_file_name);
            }
            File.WriteAllBytes(catalog_file_name, VSLib.ConvertStringToByte(ost));
        }
Esempio n. 3
0
        /// <summary>
        /// Delete key
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        private bool delete(byte[] key, long value = -1)
        {
            //VSDebug.StopPoint(Name, "SIZE");

            int nodeID = ROOT;

            while (nodeID >= 0)
            {
                int compare = VSLib.CompareKeys(key, BTree[nodeID].Key);

                if (compare < 0)
                {
                    nodeID = BTree[nodeID].Left;
                }
                else if (compare > 0)
                {
                    nodeID = BTree[nodeID].Right;
                }
                else
                {
                    return(delete_avl_node(nodeID, value));
                }
            }
            return(false);
        }
Esempio n. 4
0
        /// <summary>
        /// Set multiple string values
        /// </summary>
        /// <param name="fields">
        /// Array of fieldsin format: {name}={value}
        /// </param>
        /// <returns></returns>
        public void Set(string[] fields)
        {
            bool upd = false;

            if (fields != null)
            {
                if (fields.Length > 0)
                {
                    for (int i = 0; i < fields.Length; i++)
                    {
                        int j = fields[i].IndexOf("=");
                        if (j >= 0)
                        {
                            string name  = fields[i].Substring(0, j);
                            string value = "";
                            if (j < (fields[i].Length - 1))
                            {
                                value = fields[i].Substring(j + 1, fields[i].Length - j - 1);
                            }
                            set_field(name, FIELD_TYPE_STRING, VSLib.ConvertStringToByte(value), false);
                            upd = true;
                        }
                    }
                }
            }
            if (upd)
            {
                serialize();
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Delete existing index
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public void DeleteIndex(string name)
        {
            if (IndexSpace != null)
            {
                IndexSpace.DeleteIndex(DEFS.PrepareFullIndexName(this.Name, name));
            }
            else
            {
                VSIndex x = this.get_index(name);
                if (x == null)
                {
                    throw new VSException(DEFS.E0052_DELETE_INDEX_ERROR_CODE, "- index is not found - '" + name + "'");
                }

                // Remove all references
                VSIndex ref_x = get_index(DEFS.PrepareFullIndexName(DEFS.ParseIndexSpace(name), DEFS.INDEX_CROSS_REFERENCES));
                x.Reset();
                while (x.Next())
                {
                    long[] refs = x.CurrentRefs;
                    for (int i = 0; i < refs.Length; i++)
                    {
                        byte[] key = VSLib.ConvertLongToByte(refs[i]);
                        ref_x.delete_node(key, x.CurrentAvlNode.ID);
                    }
                }

                index_list.Remove(x);
                index_list_full.Remove(x);
                x.purge();
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Gey index names by space prefix
        /// </summary>
        /// <param name="space_name"></param>
        /// <returns></returns>
        private string[] get_indexes_for_space(string space_name)
        {
            List <string> lx = new List <string>();
            string        nm = space_name.Trim().ToLower();

            for (int i = 0; i < index_list.Count; i++)
            {
                string[] parsed_name = new string[2];
                parsed_name[0] = DEFS.ParseIndexSpace(index_list[i].Name);
                parsed_name[1] = DEFS.ParseIndexName(index_list[i].Name);
                if (nm == "")
                {
                    if (parsed_name[0] == "")
                    {
                        lx.Add(parsed_name[1]);
                    }
                }
                else
                {
                    if (VSLib.Compare(nm, parsed_name[0]))
                    {
                        lx.Add(parsed_name[1]);
                    }
                }
            }

            return(lx.ToArray());
        }
Esempio n. 7
0
        // Reader/Writer with encryption

        /// <summary>
        /// File Stream
        /// </summary>
        /// <param name="filename">File name for Read/Write</param>
        /// <param name="mode">'c' - create; 'o' - open, 'a' - append</param>
        /// <param name="key">key for encrypt/decrypt ("" - no encryption)</param>
        public VSIO(string filename, string mode, string key)
        {
            string md = mode.Trim().ToLower();

            //fs = stream;
            xkey_b   = VSLib.ConvertStringToByte(key);
            _encrypt = (key != "");
            IMO      = false;

            if (md == FILE_MODE_CREATE)
            {
                fs = new FileStream(filename, FileMode.Create);
            }
            else if (md == FILE_MODE_APPEND)
            {
                fs = new FileStream(filename, FileMode.Append);
            }
            else if (md == FILE_MODE_OPEN)
            {
                fs = new FileStream(filename, FileMode.OpenOrCreate);
            }
            else
            {
                throw new VSException(DEFS.E0034_IO_ERROR_CODE, "Invalid open mode = '" + md + "'");
            }
        }
Esempio n. 8
0
        /// <summary>
        ///  Find key (string)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="op">0 - equal; -1 - nearest left (less than); 1 - nearest right (more than) </param>
        /// <returns>Value</returns>
        public BTResult Find(string key, int op)
        {
            int      i   = find(VSLib.ConvertStringToByte(key), op);
            BTResult ret = new BTResult();

            ret.KeyString = (i < 0) ? "" : VSLib.ConvertByteToString(BTree[i].Key);
            ret.Value     = (i < 0) ? -1 : BTree[i].Value[0];

            return(ret);
        }
Esempio n. 9
0
        /// <summary>
        ///  Find key (long)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="op">0 - equal; -1 - nearest left (less than); 1 - nearest right (more than) </param>
        /// <returns>Value</returns>
        public BTResult Find(long key, int op)
        {
            int      i   = find(VSLib.ConvertLongToByteReverse(key), op);
            BTResult ret = new BTResult();

            ret.Key   = (i < 0) ? -1 : VSLib.ConvertByteToLongReverse(BTree[i].Key);
            ret.Value = (i < 0) ? -1 : BTree[i].Value[0];

            return(ret);
        }
Esempio n. 10
0
        /// <summary>
        ///  Find all keys (non-unique), string
        /// </summary>
        /// <param name="key"></param>
        /// <param name="op">0 - equal; -1 - nearest left (less than); 1 - nearest right (more than) </param>
        /// <returns></returns>
        public BTResultList FindAll(string key, int op)
        {
            int i = find(VSLib.ConvertStringToByte(key), op);

            BTResultList ret = new BTResultList();

            ret.KeyString = (i < 0) ? "" : VSLib.ConvertByteToString(BTree[i].Key);
            ret.Value     = (i < 0) ? new long[0] : BTree[i].Value.ToArray();

            return(ret);
        }
Esempio n. 11
0
        /// <summary>
        ///  Find all keys (non-unique), long
        /// </summary>
        /// <param name="key"></param>
        /// <param name="op">0 - equal; -1 - nearest left (less than); 1 - nearest right (more than) </param>
        /// <returns></returns>
        public BTResultList FindAll(long key, int op)
        {
            int i = find(VSLib.ConvertLongToByteReverse(key), op);

            BTResultList ret = new BTResultList();

            ret.Key   = (i < 0) ? -1 : VSLib.ConvertByteToLongReverse(BTree[i].Key);
            ret.Value = (i < 0) ? new long[0] : BTree[i].Value.ToArray();

            return(ret);
        }
Esempio n. 12
0
        /// <summary>
        /// Write FBQE
        /// </summary>
        /// <param name="f"></param>
        private void SerializeFBQE(FBQE f)
        {
            VSLib.CopyBytes(buffer, VSLib.ConvertStringToByte(DEFS.FBQE_SIGNATURE), f.address + SG_POS, SG_LEN);
            VSLib.CopyBytes(buffer, VSLib.ConvertLongToByte(f.ADDRESS_START), f.address + ADDRESS_START_POS, ADDRESS_START_LEN);
            VSLib.CopyBytes(buffer, VSLib.ConvertLongToByte(f.ADDRESS_END), f.address + ADDRESS_END_POS, ADDRESS_END_LEN);
            VSLib.CopyBytes(buffer, VSLib.ConvertLongToByte(f.LENGTH), f.address + LENGTH_POS, LENGTH_LEN);
            VSLib.CopyBytes(buffer, VSLib.ConvertIntToByte(f.PREV), f.address + PREV_POS, PREV_LEN);
            VSLib.CopyBytes(buffer, VSLib.ConvertIntToByte(f.NEXT), f.address + NEXT_POS, NEXT_LEN);

            F_Object.Write(f.address, VSLib.GetByteArray(buffer, f.address, FBQE_LENGTH), FBQE_LENGTH);
        }
Esempio n. 13
0
        /// <summary>
        /// Delete reference
        /// </summary>
        /// <param name="rf"></param>
        /// <returns></returns>
        internal bool delete_ref(long rf)
        {
            int         x    = -1;
            List <long> refs = REFS.ToList <long>();

            int count = refs.Count;

            for (int i = 0; i < count; i++)
            {
                int i2 = count - 1 - i;
                if (i > i2)
                {
                    break;
                }

                if (refs[i] == rf)
                {
                    x = i;
                }
                else if (refs[i2] == rf)
                {
                    x = i2;
                }

                if (x > 0)
                {
                    break;
                }
            }

            if (x < 0)
            {
                return(false);
            }
            else
            {
                refs.RemoveAt(x);
            }

            byte[] refs_new = new byte[refs.Count * 8];

            for (int i = 0; i < refs.Count; i++)
            {
                VSLib.CopyBytes(refs_new, VSLib.ConvertLongToByte(refs[i]), (i * 8), 8);
            }

            ALLOCATION.Write(this.REF_POS, refs_new, refs_new.Length);        // Write

            REF_COUNT = refs.Count;

            return(true);
        }
Esempio n. 14
0
        /// <summary>
        /// Lock current directory(all spaces)
        /// </summary>
        /// <returns></returns>
        private bool Lock()
        {
            if (IMO)
            {
                return(true);
            }

            byte[] shared_code           = { 0x0F, 0, 0, 0, 0, 0, 0, 0 };
            byte[] exclusive_code        = { 0xFF, 0, 0, 0, 0, 0, 0, 0 };
            long   exclusive_lock_offset = 0;

            string lock_fn = root_path + "\\" + DEFS.LOCK_FILE_NAME;

            if (fslck == null)
            {
                lock_number = -1;
                if (!File.Exists(lock_fn))
                {
                    byte[] b = new byte[8192];
                    fslck = File.Create(lock_fn);
                    fslck.Write(b, 0, b.Length);
                    fslck.Close();
                    fslck = null;
                }

                fslck = File.Open(lock_fn, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
                long number = fslck.Length / lock_length;

                try
                {
                    fslck.Lock(exclusive_lock_offset, fslck.Length);           // Lock for both read/write exclusively
                    lock_number = 0;
                    fslck.Seek(lock_number, SeekOrigin.Begin);
                    fslck.Write(exclusive_code, 0, exclusive_code.Length);
                    fslck.Write(VSLib.ConvertLongToByte(DateTime.Now.ToBinary()), 0, 8);
                }
                catch (IOException e)
                {
                    this.Error = e.Message;
                    fslck.Close();
                    fslck = null;
                }
                return(lock_number >= 0);
            }
            else
            {
                return(true);
            }
        }
Esempio n. 15
0
 /// <summary>
 /// Memory Stream
 /// </summary>
 /// <param name="data">byte array - existing stream (read-only); null or empty - new stream</param>
 /// <param name="key">key for encrypt/decrypt ("" - no encryption)</param>
 public VSIO(byte[] data, string key)
 {
     //fs = stream;
     xkey_b   = VSLib.ConvertStringToByte(key);
     _encrypt = (key != "");
     IMO      = true;
     if (data == null)
     {
         fs = new MemoryStream();
     }
     else
     {
         fs = new MemoryStream(data);
     }
 }
Esempio n. 16
0
        /// <summary>
        /// Delete index (byte key)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="id">For non-unique index. 0 - delete a </param>
        /// <returns></returns>
        public bool Delete(byte[] key, long id)
        {
            if (this.index_name == DEFS.INDEX_CROSS_REFERENCES)
            {
                throw new VSException(DEFS.E0055_INDEX_INVALID_OP_CODE, " - 'Delete' for '" + DEFS.INDEX_CROSS_REFERENCES + "'");
            }

            long av_id = this.delete_node(key, id);

            if (av_id > 0)
            {
                byte[] obj_key = VSLib.ConvertLongToByteReverse(id);
                XRefs.delete_node(obj_key, av_id);
                return(true);
            }
            return(false);
        }
Esempio n. 17
0
        /// <summary>
        /// Remove all related indexes for object by ID
        /// ONLY for objects and indexes located in THIS space
        /// </summary>
        /// <param name="a"></param>
        private void remove_all_indexes(string space_name, long id)
        {
            VSIndex ref_index = this.get_index(DEFS.PrepareFullIndexName(space_name, DEFS.INDEX_CROSS_REFERENCES));

            if (ref_index != null)
            {
                byte[] key = VSLib.ConvertLongToByteReverse(id);

                long[] ref_nodes = ref_index.FindAll(key, false);         // Get all avl node ids for obj id

                ref_index.delete_node(key, -1);                           // Remove reference record

                for (int i = 0; i < ref_nodes.Length; i++)
                {
                    this.get_index(this.GetAllocation(ref_nodes[i]).ReadLong(VSAvlNode.INDEX_POS)).delete_avl_node(ref_nodes[i], id);
                }
            }
        }
Esempio n. 18
0
        /// <summary>
        /// Convert long to byte array
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private static string GetAddressSignature(long value)
        {
            //string s = VSLib.ConvertLongToHexString(value);

            byte[] b = VSLib.ConvertLongToByte(value);
            int    n = 0;

            for (int i = 0; i < b.Length; i++)
            {
                n += b[i] + (i * 8);
                if (n > 9999)
                {
                    n -= 9999;
                }
            }

            return(n.ToString("D4"));
        }
Esempio n. 19
0
        /// <summary>
        /// Array of the field names by pattern
        /// </summary>
        public string[] GetFields(string pattern = "*")
        {
            sync_cache();
            if (FCache == null)
            {
                return(new string[0]);
            }

            List <string> ls = new List <string>();

            for (int i = 0; i < FCache.Count; i++)
            {
                if (VSLib.Compare(pattern, FCache[i].NAME))
                {
                    ls.Add(FCache[i].NAME);
                }
            }
            return(ls.ToArray());
        }
Esempio n. 20
0
        /// <summary>
        /// Calculate new signature
        /// </summary>
        /// <returns></returns>
        public string CalculateSignature()
        {
            ulong chs = (ulong)((Id * 123456789) - space_size_pg * 3 + extension_pg * 5 - page_size_kb * 7 + partitions * 9 - VSLib.ConvertStringToByte(CONFIG.ste)[0]);

            byte[] x = VSLib.ConvertStringToByte(indexspace.Trim().ToLower());
            for (int i = 0; i < x.Length; i++)
            {
                chs += (ulong)(x[i] * 3);
            }

            x = VSLib.ConvertStringToByte(name.Trim().ToLower());
            for (int i = 0; i < x.Length; i++)
            {
                chs += (ulong)(x[i] * 5);
            }

            signature = VSLib.ConvertULongToHexString(chs);
            return(signature);
        }
Esempio n. 21
0
        /// <summary>
        /// Empty path - In Memory Option
        /// </summary>
        /// <param name="path"></param>
        public VSCatalog(string path)
        {
            this.dl = new List <VSCatalogDescriptor>();

            if (path == "")
            {
                catalog_file_name = "";
                backup_file_name  = "";
                CATALOG_DIR       = "~IMO~";
            }
            else
            {
                catalog_file_name = path + "\\" + DEFS.CTLG_FILE_NAME;
                backup_file_name  = catalog_file_name + ".bak";

                string[] s = VSLib.Parse(path, "\\");
                CATALOG_DIR = s[s.Length - 1];

                Load();
            }
        }
Esempio n. 22
0
        internal const long FBQE_HEADER_LENGTH = LAST_Q_POS + LAST_Q_LEN + 4; // 4-reserve


        ///////////////////////////////////////////////////
        ///////////////////// METHODS /////////////////////
        ///////////////////////////////////////////////////
        /// <summary>
        /// Get by index
        /// </summary>
        /// <param name="idx"></param>
        /// <returns></returns>
        public FBQE GetFBQE(int idx)
        {
            FBQE f = new FBQE();

            f.index   = idx;
            f.address = idx * FBQE_LENGTH;

            f.SG            = VSLib.ConvertByteToString(VSLib.GetByteArray(buffer, f.address + SG_POS, SG_LEN));
            f.ADDRESS_START = VSLib.ConvertByteToLong(VSLib.GetByteArray(buffer, f.address + ADDRESS_START_POS, ADDRESS_START_LEN));
            f.ADDRESS_END   = VSLib.ConvertByteToLong(VSLib.GetByteArray(buffer, f.address + ADDRESS_END_POS, ADDRESS_END_LEN));
            f.LENGTH        = VSLib.ConvertByteToLong(VSLib.GetByteArray(buffer, f.address + LENGTH_POS, LENGTH_LEN));
            f.PREV          = VSLib.ConvertByteToInt(VSLib.GetByteArray(buffer, f.address + PREV_POS, PREV_LEN));
            f.NEXT          = VSLib.ConvertByteToInt(VSLib.GetByteArray(buffer, f.address + NEXT_POS, NEXT_LEN));

            if (f.SG != DEFS.FBQE_SIGNATURE)
            {
                throw new VSException(DEFS.E0006_INVALID_SIGNATURE_CODE, "(FBQE)");
            }

            return(f);
        }
Esempio n. 23
0
        /// <summary>
        /// Reset by key
        /// </summary>
        /// <param name="key"></param>
        public void Reset(long key, bool partial = false)
        {
            _current = -1;

            if (ROOT >= 0)
            {
                int id = find(VSLib.ConvertLongToByteReverse(key), (partial? 1 : 0));
                if (id >= 0)
                {
                    _action  = Action.Current;
                    _current = _right = id;
                }
                else
                {
                    _action = Action.End;
                }
            }
            else
            {
                _action = Action.End;
            }
        }
Esempio n. 24
0
        /// <summary>
        /// Display space/storage name
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public string[] List(string name = "*")
        {
            List <string> l = new List <string>();

            string[] sp_list = GetSpaceNameList();

            bool nf = false;
            long sz = GetStorageSize();

            if (name == "*")
            {
                l.Add("Storage size:   " + sz.ToString("#,#;(#,#)") + " bytes (" + (sz / 1048576).ToString("#,#;(#,#)") + " Mb)");
            }

            for (int i = 0; i < sp_list.Length; i++)
            {
                if (l.Count > 0)
                {
                    l.Add(" ");
                }
                if (VSLib.Compare(name.Trim().ToLower(), sp_list[i]))
                {
                    nf = true;
                    string[] info = GetSpaceHeaderInfo(sp_list[i]);
                    for (int j = 0; j < info.Length; j++)
                    {
                        l.Add(info[j]);
                    }
                }
            }

            if (!nf)
            {
                l.Add("No space found matching search criteria '" + name + "'");
            }

            return(l.ToArray());
        }
Esempio n. 25
0
        /// <summary>
        /// Remove space: name: space name
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public void Remove(string name)
        {
            if (state != DEFS.STATE_DEFINED)
            {
                throw new VSException(DEFS.E0025_STORAGE_UNABLE_TO_COMPLETE_CODE, "- 'Remove' (storage is opened or undefined)");
            }

            if (!this.Lock())
            {
                throw new VSException(DEFS.E0001_UNABLE_TO_LOCK_CODE, "(Remove space)");
            }

            for (int n = (CATALOG.Count - 1); n >= 0; n--)
            {
                if (VSLib.Compare(name, CATALOG[n].Name))
                {
                    if (!IMO)
                    {
                        for (int i = 0; i < CATALOG[n].Partitions; i++)
                        {
                            System.IO.File.Delete(GetSpaceFileName(CATALOG[n].Name, CATALOG[n].Path, i));
                        }

                        FileStream fp = new FileStream(TransactionFileName, FileMode.Create);
                        fp.Close();
                        CATALOG.Delete(CATALOG[n].Name);
                        CATALOG.Save();
                    }
                    else
                    {
                        CATALOG.Delete(CATALOG[n].Name);
                    }
                }
            }
            Release();
        }
Esempio n. 26
0
 /// <summary>
 /// Delete index (string key)
 /// </summary>
 /// <param name="key"></param>
 /// <param name="id">For non-unique index. 0 - delete a </param>
 /// <returns></returns>
 public bool Delete(string key, long id)
 {
     byte[] k = VSLib.ConvertStringToByte(key);
     return(this.Delete(k, id));
 }
Esempio n. 27
0
 /// <summary>
 /// Check if node exists (string key)
 /// </summary>
 /// <param name="key"></param>
 /// <param name="partial"></param>
 /// <returns>true/false</returns>
 public bool Exists(string key, bool partial = false)
 {
     return(Exists(VSLib.ConvertStringToByte(key), partial));
 }
Esempio n. 28
0
 /// <summary>
 /// Get ID by full or partial string key (first if if non-unique index)
 /// </summary>
 /// <param name="k"></param>
 /// <returns></returns>
 public long Find(string key, bool partial = false)
 {
     byte[] k = VSLib.ConvertStringToByte(key);
     return(Find(k, partial));
 }
Esempio n. 29
0
 /// <summary>
 /// Read u=short
 /// </summary>
 /// <param name="address"></param>
 /// <returns></returns>
 public ushort ReadUShort(long address)
 {
     return(VSLib.ConvertByteToUShort(ReadBytes(address, 2)));
 }
Esempio n. 30
0
 /// <summary>
 /// Read long
 /// </summary>
 /// <param name="address"></param>
 /// <returns></returns>
 public long ReadLong(long address)
 {
     return(VSLib.ConvertByteToLong(ReadBytes(address, 8)));
 }