internal void WriteByteArrayTo(IJournalledResource data, long position, byte[] buf, int off, int len)
        {
            if (PARANOID_CHECKS)
            {
                lock (write_lock) {
                    if (write_lock_count == 0)
                    {
                        Console.Out.WriteLine("Write without a Lock!");
                        Console.Out.WriteLine(new ApplicationException().StackTrace);
                    }
                }
            }

            long page_number  = position / page_size;
            int  start_offset = (int)(position % page_size);
            int  to_write     = System.Math.Min(len, page_size - start_offset);

            BMPage page = FetchPage(data, page_number);

            lock (page) {
                try {
                    page.Initialize();
                    page.Write(start_offset, buf, off, to_write);
                } finally {
                    page.Dispose();
                }
            }
            len -= to_write;

            while (len > 0)
            {
                off      += to_write;
                position += to_write;
                ++page_number;
                to_write = System.Math.Min(len, page_size);

                page = FetchPage(data, page_number);
                lock (page) {
                    try {
                        page.Initialize();
                        page.Write(0, buf, off, to_write);
                    } finally {
                        page.Dispose();
                    }
                }
                len -= to_write;
            }
        }
        internal void WriteByteTo(IJournalledResource data,
                                  long position, int b)
        {
            if (PARANOID_CHECKS)
            {
                lock (write_lock) {
                    if (write_lock_count == 0)
                    {
                        Console.Out.WriteLine("Write without a Lock!");
                        Console.Out.WriteLine(new ApplicationException().StackTrace);
                    }
                }
            }

            long page_number = position / page_size;

            BMPage page = FetchPage(data, page_number);

            lock (page) {
                try {
                    page.Initialize();
                    page.Write((int)(position % page_size), (byte)b);
                } finally {
                    page.Dispose();
                }
            }
        }
        internal int ReadByteArrayFrom(IJournalledResource data, long position, byte[] buf, int off, int len)
        {
            int  orig_len     = len;
            long page_number  = position / page_size;
            int  start_offset = (int)(position % page_size);
            int  to_read      = System.Math.Min(len, page_size - start_offset);

            BMPage page = FetchPage(data, page_number);

            lock (page) {
                try {
                    page.Initialize();
                    page.Read(start_offset, buf, off, to_read);
                } finally {
                    page.Dispose();
                }
            }

            len -= to_read;
            while (len > 0)
            {
                off      += to_read;
                position += to_read;
                ++page_number;
                to_read = System.Math.Min(len, page_size);

                page = FetchPage(data, page_number);
                lock (page) {
                    try {
                        page.Initialize();
                        page.Read(0, buf, off, to_read);
                    } finally {
                        page.Dispose();
                    }
                }
                len -= to_read;
            }

            return(orig_len);
        }
        // ------
        // Buffered access methods.  These are all thread safe methods.  When a page
        // is accessed the page is synchronized so no 2 or more operations can
        // Read/Write from the page at the same time.  An operation can Read/Write to
        // different pages at the same time, however, and this requires thread safety
        // at a lower level (in the IJournalledResource implementation).
        // ------

        internal int ReadByteFrom(IJournalledResource data, long position)
        {
            long page_number = position / page_size;
            int  v;

            BMPage page = FetchPage(data, page_number);

            lock (page) {
                try {
                    page.Initialize();
                    v = ((int)page.Read((int)(position % page_size))) & 0x0FF;
                } finally {
                    page.Dispose();
                }
            }

            return(v);
        }
        /// <summary>
        /// Called when a new page is created.
        /// </summary>
        /// <param name="page"></param>
        private void PageCreated(BMPage page)
        {
            // lock (T_lock) {

            if (PARANOID_CHECKS)
            {
                int i = page_list.IndexOf(page);
                if (i != -1)
                {
                    BMPage f = (BMPage)page_list[i];
                    if (f == page)
                    {
                        throw new ApplicationException("Same page added multiple times.");
                    }
                    if (f != null)
                    {
                        throw new ApplicationException("Duplicate pages.");
                    }
                }
            }

            page.t = current_T;
            ++current_T;

            ++current_page_count;
            page_list.Add(page);

            // Below is the page purge algorithm.  If the maximum number of pages
            // has been created we sort the page list weighting each page by time
            // since last accessed and total number of accesses and clear the bottom
            // 20% of this list.

            // Check if we should purge old pages and purge some if we do...
            if (current_page_count > max_pages)
            {
                // Purge 20% of the cache
                // Sort the pages by the current formula,
                //  ( 1 / page_access_count ) * (current_t - page_t)
                // Further, if the page has written data then we multiply by 0.75.
                // This scales down page writes so they have a better chance of
                // surviving in the cache than page writes.
                Object[] pages = page_list.ToArray();
                Array.Sort(pages, PageCacheComparer);

                int purge_size = System.Math.Max((int)(pages.Length * 0.20f), 2);
                for (int i = 0; i < purge_size; ++i)
                {
                    BMPage dpage = (BMPage)pages[pages.Length - (i + 1)];
                    lock (dpage) {
                        dpage.Dispose();
                    }
                }

                // Remove all the elements from page_list and set it with the sorted
                // list (minus the elements we removed).
                page_list.Clear();
                for (int i = 0; i < pages.Length - purge_size; ++i)
                {
                    page_list.Add(pages[i]);
                }

                current_page_count -= purge_size;
            }
            // }
        }