/// <summary>
        /// Dynamically extend partition (single-partition space only).
        /// Return: 0 - successful; -1 - error.
        /// </summary>
        /// <returns></returns>
        public int Extend()
        {
            if (DESCRIPTOR.Partitions > 1)
            {
                this.error = "Cannot extend multi-partition space";
                return(-1);
            }
            //Check if extension is defined while creating space
            else if (DESCRIPTOR.Extension == 0)
            {
                this.error = "Dynamic extension is not defined for this space";
                return(-1);
            }


            // Initialize new segment
            int nidx = segments_used;

            segments_used++;
            segment_table[nidx] = new page_segment_descriptor();

            // Set start-end pages
            segment_table[nidx].start_page = segment_table[nidx - 1].end_page + 1;
            segment_table[nidx].end_page   = segment_table[nidx].start_page + DESCRIPTOR.extension_pg - 1;

            // Create new page table
            long stp = segment_table[nidx].start_page;

            segment_table[nidx].page_table = new page_descriptor[DESCRIPTOR.extension_pg];
            for (int i = 0; i < segment_table[nidx].page_table.Length; i++)
            {
                if (imo)
                {
                    segment_table[nidx].page_table[i].page_state = DEFS.PAGE_READ;
                    segment_table[nidx].page_table[i].page_lock  = DEFS.PAGE_LOCKED;
                    segment_table[nidx].page_table[i].bufno      = (int)(stp + i);                  // For IMO bufno = page table element
                }
                else
                {
                    segment_table[nidx].page_table[i].page_state = DEFS.PAGE_FREE;
                    segment_table[nidx].page_table[i].page_lock  = DEFS.PAGE_UNLOCKED;
                    segment_table[nidx].page_table[i].bufno      = -1;
                }
            }


            //Add extension information
            short n      = ReadShort(DEFS.SYSTEM_ALLOCATION_ADDRESS);                           // The number of pending updates
            long  e_addr = DEFS.SYSTEM_ALLOCATION_ADDRESS + 2 + (n * 16);

            //Write(e_addr, DESCRIPTOR.Size);
            Write(e_addr, DESCRIPTOR.SpaceSize);                                                // Start addrress
            long end_address = DESCRIPTOR.SpaceSize + DESCRIPTOR.Extension;                     // End address + 1

            Write(e_addr + 8, end_address);

            // Update number of extensions
            n++;
            Write(DEFS.SYSTEM_ALLOCATION_ADDRESS, (short)n);

            // Save old physical size
            long old_size = DESCRIPTOR.SysSpaceSize;

            // Increase space size
            DESCRIPTOR.space_size_pg += DESCRIPTOR.extension_pg;


            if (imo)    // In Memory Option extension
            {
                // Create new cache (IMO)
                segment_table[nidx].cache = new page_buffer[DESCRIPTOR.extension_pg];
                for (int i = 0; i < segment_table[nidx].cache.Length; i++)
                {
                    segment_table[nidx].cache[i].buf          = new byte[page_size];
                    segment_table[nidx].cache[i].tbuf         = null;
                    segment_table[nidx].cache[i].last_access  = 0;
                    segment_table[nidx].cache[i].access_count = 0;
                    segment_table[nidx].cache[i].page_no      = i;             // Page no = Buf no
                    segment_table[nidx].cache[i].prev         = -1;
                    segment_table[nidx].cache[i].next         = -1;
                }
            }
            else
            {
                // Append pages to the physical storage
                byte[] resv      = new byte[8];
                byte[] dataArray = new byte[page_size];

                try
                {
                    for (int i = 0; i < DESCRIPTOR.extension_pg; i++)
                    {
                        long addr = old_size + (i * (DESCRIPTOR.PageSize + DEFS.SYSTEM_USED_PAGE_SPACE));

                        pd[0].fs.Write(addr, DEFS.DATA_NOT_ENCRYPTED);              // + 0 (4) Encryption indicator

                        pd[0].fs.Write(-1, (uint)0);                                // + 4 (4) CRC32 placeholder

                        pd[0].fs.Write(-1, ref resv);                               // +8 (8) reserve

                        pd[0].fs.Write(-1, ref dataArray);
                    }
                }
                catch (Exception e)
                {
                    this.error = "Error while extending file: " + e.Message;
                    return(-1);
                }

                // Increase file descriptor pages
                pd[0].end_page += DESCRIPTOR.extension_pg;

                DESCRIPTOR.CATALOG.Save();
                if (DESCRIPTOR.SysSpaceSize != pd[0].fs.GetLength())
                {
                    throw new Exception("Descriptor and file size doesnt match: " + DESCRIPTOR.SysSpaceSize.ToString() + " - " + pd[0].fs.GetLength().ToString());
                }
            }
            return(0);
        }
        /// <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("");
        }