/// <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(); } }
// 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 + "'"); } }
/// <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)); }
/// <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); }
/// <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); }
/// <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); }
/// <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); } }
/// <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); }
/// <summary> /// Open new address space: _path - root folder path; _cat_file - catalog file name; idx - index in the catalog. Return: space id or -1(if error) /// </summary> /// <param name="_path"></param> /// <param name="_cat_file"></param> /// <param name="idx"></param> /// <returns></returns> public string Open(VSCatalogDescriptor desc, VSTransaction ta) { this.DESCRIPTOR = desc; this.page_size = DESCRIPTOR.PageSize; this.imo = DESCRIPTOR.IMO; e_key = VSLib.ConvertStringToByte(DEFS.ENCRYPT_SPACE); e_buf = new byte[page_size]; _ta = ta; ////////////////////////////////////////////// ///////////// Initiate partitions //////////// ////////////////////////////////////////////// this.pd = new partition_descriptor[DESCRIPTOR.Partitions]; //Check if all partitions exists if (imo) { pd[0].start_page = 0; //Start Page pd[0].end_page = DESCRIPTOR.space_size_pg - 1; //End Page } else { for (int i = 0; i < DESCRIPTOR.Partitions; i++) { short j = (short)(i + 1); pd[i].file_name = DESCRIPTOR.Path + "\\" + DEFS.SPACE_FILE_NAME(DESCRIPTOR.Name, i); if (!System.IO.File.Exists(pd[i].file_name)) { return("Error: space file is not found - " + pd[i].file_name); } //FileStream f = null; //f = File.Open(pd[i].file_name, FileMode.Open, FileAccess.ReadWrite, FileShare.None); pd[i].fs = new VSIO(pd[i].file_name, VSIO.FILE_MODE_OPEN, DEFS.ENCRYPT_SPACE); pd[i].fs.SetEncryption(false); pd[i].start_page = (i == 0) ? 0 : pd[i - 1].end_page + 1; //Start Page pd[i].end_page = pd[i].start_page + ((pd[i].fs.GetLength()) / page_size) - 1; //End Page } } ////////////////////////////////////////////// ///////////// Initiate cache ///////////////// ////////////////////////////////////////////// // Create segment table segment_table = new page_segment_descriptor[DEFS.PAGE_SEGMENTS_NUMBER]; // Create page table for 1st segment segment_table[0] = new page_segment_descriptor(); segment_table[0].start_page = 0; segment_table[0].end_page = DESCRIPTOR.space_size_pg - 1; // Set initial number of segments used segments_used = 1; // Calculate cache size in pages if (imo) { cache_size_pages = DESCRIPTOR.space_size_pg; cache_size_bytes = DESCRIPTOR.SpaceSize; } else { string confs = VSLib.VSGetKey(DEFS.VM_CACHE_SIZE_KEY); if (confs == "") { confs = DEFS.VM_CACHE_SIZE_DEFAULT; VSLib.VSSetKey(DEFS.VM_CACHE_SIZE_KEY, confs); } int csize = VSLib.ConvertStringToInt(confs); cache_size_bytes = ((csize < 2) ? 2 : csize) * 1048576; if (cache_size_bytes < (page_size * 10)) { cache_size_bytes = page_size * 10; } cache_size_pages = (cache_size_bytes / page_size) + 1; } // Initialize cache segment_table[0].cache = new page_buffer[cache_size_pages]; for (int i = 0; i < cache_size_pages; i++) { segment_table[0].cache[i].buf = new byte[page_size]; segment_table[0].cache[i].tbuf = null; segment_table[0].cache[i].last_access = 0; segment_table[0].cache[i].access_count = 0; if (imo) { segment_table[0].cache[i].page_no = i; // Page no = Buf no segment_table[0].cache[i].prev = -1; segment_table[0].cache[i].next = -1; } else { segment_table[0].cache[i].page_no = -1; segment_table[0].cache[i].prev = i - 1; segment_table[0].cache[i].next = ((i + 1) == cache_size_pages) ? -1 : i + 1; } } // Initialize queues (not IMO) if (!imo) { q_read = new int[3]; q_read[number] = 0; q_read[first] = -1; q_read[last] = -1; q_write = new int[3]; q_write[number] = 0; q_write[first] = -1; q_write[last] = -1; q_free = new int[3]; q_free[number] = (int)cache_size_pages; q_free[first] = 0; q_free[last] = (int)cache_size_pages - 1; } ////////////////////////////////////////////// ///////////// Initiate page table //////////// ////////////////////////////////////////////// segment_table[0].page_table = new page_descriptor[DESCRIPTOR.space_size_pg]; for (int i = 0; i < segment_table[0].page_table.Length; i++) { if (imo) { segment_table[0].page_table[i].page_state = DEFS.PAGE_READ; segment_table[0].page_table[i].page_lock = DEFS.PAGE_LOCKED; segment_table[0].page_table[i].bufno = i; // For IMO bufno = page table element } else { segment_table[0].page_table[i].page_state = DEFS.PAGE_FREE; segment_table[0].page_table[i].page_lock = DEFS.PAGE_UNLOCKED; segment_table[0].page_table[i].bufno = -1; } } // Set state 'Opened' this.state = DEFS.SPACE_OPENED; // For IMO: write owner 'undefined'; otherwise: load and lock page 0. if (imo) { this.Write(DEFS.SYSTEM_OWNER_ADDRESS, DEFS.SYSTEM_OWNER_UNDEFINED.PadRight((int)DEFS.SYSTEM_OWNER_LENGTH)); } else { fetch(0, 0, 0, DEFS.PAGE_READ); this.lock_page(0); // lock 1st page } return(""); }
/// <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)); }
/// <summary> /// Add key (string) /// </summary> /// <param name="Value"></param> /// <returns></returns> public bool Insert(string key, long value) { return(this.insert(VSLib.ConvertStringToByte(key), value)); }
/// <summary> /// Write data (string) /// </summary> /// <param name="address"></param> /// <param name="data"></param> /// <returns></returns> public void Write(long address, string data) { Write(address, VSLib.ConvertStringToByte(data), data.Length); }
/// <summary> /// Serialize all fields to object fields space /// Free excessive chunks if needed /// </summary> private void serialize() { // If no fields if (FCache.Count == 0) { set_alloc(ALLOC_RESET); FIELDS_SIZE = 0; FreeSpace = -1; FCache = null; return; } byte[] b = null; int old_size = (int)(FIELDS_SIZE); // Current used size (number of fields + all fields size) // 1. Calculate new size int new_size = FIRST_FIELD_OFFSET_REL; for (int i = 0; i < FCache.Count; i++) { new_size += FCache[i].FULL_LENGTH; } // 2. Check if space is availabe, extend if required if (new_size > (old_size + FreeSpace)) { sp.Extend(this, (new_size - old_size)); } // 3. Check if only value update is required List <FieldCache> afc = FCache; FCache = new List <FieldCache>(32); int array_size = (new_size > old_size) ? new_size : old_size; byte[] data = new byte[array_size]; int data_pos = FIRST_FIELD_OFFSET_REL; VSLib.CopyBytes(data, VSLib.ConvertIntToByte(new_size), FIELDS_SIZE_POS, FIELDS_SIZE_LEN); // Size VSLib.CopyBytes(data, VSLib.ConvertShortToByte((short)afc.Count), FIELDS_NUMBER_POS, FIELDS_NUMBER_LEN); // Fields number FCache.Clear(); for (int i = 0; i < afc.Count; i++) { FieldCache fc_element = afc[i]; if (!fc_element.DELETED) { fc_element.OFFSET = data_pos; // Type data[data_pos] = fc_element.TYPE; data_pos++; // Name length data[data_pos] = (byte)(fc_element.NAME.Length); data_pos++; // Name b = VSLib.ConvertStringToByte(fc_element.NAME); VSLib.CopyBytes(data, b, data_pos, b.Length); data_pos += b.Length; // Value if (FIELD_COMPRESS[fc_element.TYPE]) // int/long/decimal { data[data_pos] = (byte)(fc_element.LENGTH); data_pos++; } else if ((fc_element.TYPE == FIELD_TYPE_BYTES) | (fc_element.TYPE == FIELD_TYPE_STRING)) { b = compress(VSLib.ConvertLongToByte(fc_element.LENGTH), FIELD_TYPE_LONG); data[data_pos] = (byte)b.Length; data_pos++; if (b.Length > 0) { VSLib.CopyBytes(data, b, data_pos, b.Length); data_pos += b.Length; } } // Write value if (fc_element.VALUE.Length > 0) { VSLib.CopyBytes(data, fc_element.VALUE, data_pos, fc_element.VALUE.Length); data_pos += afc[i].LENGTH; } // Shift offset and add to cache fc_element.OLDLENGTH = afc[i].LENGTH; fc_element.STATE = STATE_LOADED; FCacheTree.Insert(fc_element.NAME, FCache.Count); FCache.Add(fc_element); } } base.Write(base.FIXED, data, data.Length); set_alloc(ALLOC_RENEW); // Fill the rest by zeros if le length < old length if (new_size < old_size) { long full_used_size = base.FIXED + new_size; b = new byte[old_size - new_size]; base.Write(full_used_size, b, b.Length); // Fill be zeros unused space // Multiple chunks, chech if there are exessive if (base.Chunk > 0) { sp.ShrinkObject(this, full_used_size); } } FreeSpace = (int)(this.Size - base.FIXED - new_size); }
/// <summary> /// Set string value /// </summary> /// <param name="name"></param> /// <returns></returns> public void Set(string name, string value) { set_field(name, FIELD_TYPE_STRING, VSLib.ConvertStringToByte(value)); }
/// <summary> /// Load catalog content /// </summary> public void Load() { int pos = 0; string s = ""; err_line = -1; if (!File.Exists(catalog_file_name)) { File.WriteAllBytes(catalog_file_name, VSLib.ConvertStringToByte(sg_ok + DEFS.VSTORAGE_VERSION + "$" + "U" + "$" + DEFS.DELIM_NEWLINE)); // 'U' dont encrypt (+19) } s = VSLib.ConvertByteToString(File.ReadAllBytes(catalog_file_name)); // Parse file pos = s.IndexOf(DEFS.DELIM_NEWLINE); if (pos < 0) { return; } // Get encryption status string sth = s.Substring(0, pos); if (sth.Length < 21) { err_line = 1; } else { // Encryption ste = sth.Substring(19, 1).ToUpper(); ENCRYPT = (sth.Substring(19, 1) == "E"); pos += DEFS.DELIM_NEWLINE.Length; line = 1; VSCatalogDescriptor desc = null; while ((pos < s.Length) & (err_line < 0)) { string par = ""; int ln = 0; int new_pos = s.IndexOf(DEFS.DELIM_NEWLINE, pos); if (new_pos < 0) { ln = s.Length - pos; } else { ln = new_pos - pos; } if (ln > 0) { par = s.Substring(pos, ln).Trim(); } line++; pos += (ln + DEFS.DELIM_NEWLINE.Length); if (par != "") { if (par.Substring(0, 1) == "[") { // Parse new descriptor desc = null; string sname = ""; int eb = par.IndexOf("]", 1); if (eb < 0) { err_line = line; } else { if (eb > 1) { sname = par.Substring(1, eb - 1); } if (sname != "") { sname = sname.Trim().ToLower(); for (int i = 0; i < dl.Count; i++) { if (dl[i].Name == sname) { desc = dl[i]; break; } } if (desc == null) { desc = new VSCatalogDescriptor(this); desc.name = sname; dl.Add(desc); } } else { err_line = line; } } } else { // Parse parameters string s_val = ""; long n_val = 0; if (desc == null) { err_line = line; } else { par += " "; if (parse_long(ref par, DEF_ID, out n_val)) { if (n_val >= 0) { desc.id = (short)n_val; } } else if (parse_long(ref par, DEF_SIZE, out n_val)) { if (n_val >= 0) { desc.space_size_pg = n_val; } } else if (parse_long(ref par, DEF_EXTENSION, out n_val)) { if (n_val >= 0) { desc.extension_pg = n_val; } } else if (parse_long(ref par, DEF_PAGESIZE, out n_val)) { if (n_val >= 0) { desc.page_size_kb = n_val; } } else if (parse_long(ref par, DEF_PARTITIONS, out n_val)) { if (n_val >= 0) { desc.partitions = n_val; } } else if (parse_string(ref par, DEF_PATH, out s_val)) { if (s_val != DEF_ERROR) { desc.path = s_val; } } else if (parse_string(ref par, DEF_INDEXSPACE, out s_val)) { if (s_val != DEF_ERROR) { desc.indexspace = s_val; } } else if (parse_string(ref par, DEF_TIMESTAMP, out s_val)) { if (s_val != DEF_ERROR) { desc.creation_timestamp = s_val; } } else if (parse_string(ref par, DEF_SIGNATURE, out s_val)) { if (s_val != DEF_ERROR) { desc.signature = s_val; } } else { err_line = line; } } } } } } if (err_line >= 0) { throw new VSException(DEFS.E0016_OPEN_STORAGE_ERROR_CODE, "- invalid catalog entry at line " + err_line.ToString()); } for (int i = 0; i < dl.Count; i++) { string sg = dl[i].Signature; dl[i].CalculateSignature(); if (dl[i].Signature != sg) { throw new VSException(DEFS.E0016_OPEN_STORAGE_ERROR_CODE, "- missing or invalid space signature for '" + dl[i].Name); } } }
/// <summary> /// Write string /// </summary> /// <param name="offset"></param> /// <param name="data"></param> public void Write(long offset, string data) { byte[] b = VSLib.ConvertStringToByte(data); this.Write(offset, ref b); }
/// <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)); }
/// <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)); }
/// <summary> /// Delete key (string) /// </summary> /// <param name="key"></param> /// <returns></returns> public bool Delete(string key, long value = -1) { return(this.delete(VSLib.ConvertStringToByte(key), value)); }