/// <summary>
        /// Finds or creates a new ID for the graph with the specified graph URI
        /// </summary>
        /// <param name="graphUri">The graph URI to lookup</param>
        /// <param name="profiler"></param>
        /// <returns>The ID assigned to the graph</returns>
        public int AssertGraphId(string graphUri, BrightstarProfiler profiler = null)
        {
            if (String.IsNullOrEmpty(graphUri))
            {
                throw new ArgumentException("Graph URI must not be null or an empty string", "graphUri");
            }
            if (graphUri.Length > short.MaxValue)
            {
                throw new ArgumentException(
                          String.Format("Graph URI string exceeds maximum allowed length of {0} bytes", short.MaxValue), "graphUri");
            }
#if WINDOWS_PHONE || PORTABLE
            lock (_lock)
            {
                int entryId;
                if (_graphUriIndex.TryGetValue(graphUri, out entryId) && !_allEntries[entryId].IsDeleted)
                {
                    return(entryId);
                }
                var newId = _allEntries.Count;
                var entry = new GraphIndexEntry(newId, graphUri, false);
                _allEntries.Add(entry);
                _graphUriIndex.Add(graphUri, newId);
                return(newId);
            }
#else
            using (profiler.Step("Assert Graph Id"))
            {
                _lock.EnterUpgradeableReadLock();
                try
                {
                    int entryId;
                    if (_graphUriIndex.TryGetValue(graphUri, out entryId) && !_allEntries[entryId].IsDeleted)
                    {
                        return(entryId);
                    }
                    _lock.EnterWriteLock();
                    try
                    {
                        var newId = _allEntries.Count;
                        var entry = new GraphIndexEntry(newId, graphUri, false);
                        _allEntries.Add(entry);
                        _graphUriIndex.Add(graphUri, newId);
                        return(newId);
                    }
                    finally
                    {
                        _lock.ExitWriteLock();
                    }
                }
                finally
                {
                    _lock.ExitUpgradeableReadLock();
                }
            }
#endif
        }
        void Read(ulong rootPageId, BrightstarProfiler profiler)
        {
            IPage currentPage = _pageStore.Retrieve(rootPageId, profiler);
            int   offset      = 0;
            int   entryIndex  = 0;

            while (true)
            {
                var marker = currentPage.Data[offset++];
                if (marker == 0xff)
                {
                    ulong nextPageId = BitConverter.ToUInt64(currentPage.Data, _pageStore.PageSize - 8);
                    if (nextPageId == 0)
                    {
                        return;
                    }
                    currentPage = _pageStore.Retrieve(nextPageId, profiler);
                    offset      = 0;
                }
                else if (marker == 2)
                {
                    _allEntries.Add(new GraphIndexEntry(entryIndex++, null, true));
                }
                else
                {
                    int uriByteLength = BitConverter.ToInt32(currentPage.Data, offset);
                    offset += 4;
                    var uri = Encoding.UTF8.GetString(currentPage.Data, offset, uriByteLength);
                    offset += uriByteLength;
                    var newEntry = new GraphIndexEntry(entryIndex++, uri, marker == 1);
                    _allEntries.Add(newEntry);
                    if (!newEntry.IsDeleted)
                    {
                        _graphUriIndex[newEntry.Uri] = newEntry.Id;
                    }
                }
            }
        }
        public void DeleteGraph(int graphId)
        {
#if WINDOWS_PHONE || PORTABLE
            lock (_lock)
            {
                if (graphId < _allEntries.Count)
                {
                    var toDelete = _allEntries[graphId];
                    if (!toDelete.IsDeleted)
                    {
                        _allEntries[graphId] = new GraphIndexEntry(toDelete.Id, toDelete.Uri, true);
                        _graphUriIndex.Remove(toDelete.Uri);
                        IsDirty = true;
                    }
                }
            }
#else
            _lock.EnterWriteLock();
            try
            {
                if (graphId < _allEntries.Count)
                {
                    var toDelete = _allEntries[graphId];
                    if (!toDelete.IsDeleted)
                    {
                        _allEntries[graphId] = new GraphIndexEntry(toDelete.Id, toDelete.Uri, true);
                        _graphUriIndex.Remove(toDelete.Uri);
                        IsDirty = true;
                    }
                }
            }
            finally
            {
                _lock.ExitWriteLock();
            }
#endif
        }
 void Read(ulong rootPageId, BrightstarProfiler profiler)
 {
     byte[] currentPage = _pageStore.Retrieve(rootPageId, profiler);
     int offset = 0;
     int entryIndex = 0;
     while(true)
     {
         var marker = currentPage[offset++];
         if (marker == 0xff)
         {
             ulong nextPageId = BitConverter.ToUInt64(currentPage, _pageStore.PageSize - 8);
             if (nextPageId == 0) return;
             currentPage = _pageStore.Retrieve(nextPageId, profiler);
             offset = 0;
         }
         else if (marker == 2)
         {
             _allEntries.Add(new GraphIndexEntry(entryIndex++, null, true));
         }
         else
         {
             int uriByteLength = BitConverter.ToInt32(currentPage, offset);
             offset += 4;
             var uri = Encoding.UTF8.GetString(currentPage, offset, uriByteLength);
             offset += uriByteLength;
             var newEntry = new GraphIndexEntry(entryIndex++, uri, marker == 1);
             _allEntries.Add(newEntry);
             if (!newEntry.IsDeleted) _graphUriIndex[newEntry.Uri] = newEntry.Id;
         }
     }
 }
        public void DeleteGraph(int graphId)
        {
#if WINDOWS_PHONE
            lock(_lock)
            {
                if (graphId < _allEntries.Count)
                {
                    var toDelete = _allEntries[graphId];
                    if (!toDelete.IsDeleted)
                    {
                        _allEntries[graphId] = new GraphIndexEntry(toDelete.Id, toDelete.Uri, true);
                        _graphUriIndex.Remove(toDelete.Uri);
                        IsDirty = true;
                    }
                }                
            }
#else
            _lock.EnterWriteLock();
            try
            {
                if (graphId < _allEntries.Count)
                {
                    var toDelete = _allEntries[graphId];
                    if (!toDelete.IsDeleted)
                    {
                        _allEntries[graphId] = new GraphIndexEntry(toDelete.Id, toDelete.Uri, true);
                        _graphUriIndex.Remove(toDelete.Uri);
                        IsDirty = true;
                    }
                }
            }
            finally
            {
                _lock.ExitWriteLock();
            }
#endif
        }
        /// <summary>
        /// Finds or creates a new ID for the graph with the specified graph URI
        /// </summary>
        /// <param name="graphUri">The graph URI to lookup</param>
        /// <param name="profiler"></param>
        /// <returns>The ID assigned to the graph</returns>
        public int AssertGraphId(string graphUri, BrightstarProfiler profiler = null)
        {
            if (String.IsNullOrEmpty(graphUri))
            {
                throw new ArgumentException("Graph URI must not be null or an empty string", "graphUri");
            }
            if (graphUri.Length > short.MaxValue)
            {
                throw new ArgumentException(
                    String.Format("Graph URI string exceeds maximum allowed length of {0} bytes", short.MaxValue), "graphUri");
            }
#if WINDOWS_PHONE
            lock(_lock)
            {
                int entryId;
                if (_graphUriIndex.TryGetValue(graphUri, out entryId) && !_allEntries[entryId].IsDeleted)
                {
                    return entryId;
                }
                var newId = _allEntries.Count;
                var entry = new GraphIndexEntry(newId, graphUri, false);
                _allEntries.Add(entry);
                _graphUriIndex.Add(graphUri, newId);
                return newId;                
            }
#else
            using (profiler.Step("Assert Graph Id"))
            {
                _lock.EnterUpgradeableReadLock();
                try
                {
                    int entryId;
                    if (_graphUriIndex.TryGetValue(graphUri, out entryId) && !_allEntries[entryId].IsDeleted)
                    {
                        return entryId;
                    }
                    _lock.EnterWriteLock();
                    try
                    {
                        var newId = _allEntries.Count;
                        var entry = new GraphIndexEntry(newId, graphUri, false);
                        _allEntries.Add(entry);
                        _graphUriIndex.Add(graphUri, newId);
                        return newId;
                    }
                    finally
                    {
                        _lock.ExitWriteLock();
                    }
                }
                finally
                {
                    _lock.ExitUpgradeableReadLock();
                }
            }
#endif
        }