using System; using System.Collections.Generic; using System.IO; using ZeroLevel.Services.Memory; using ZeroLevel.Services.Serialization; namespace ZeroLevel.Services.PartitionStorage { internal sealed class StorePartitionSparseIndex : IStorePartitionIndex { private readonly StoreFilePartition _filePartition; private readonly Func _keyComparer; private readonly string _indexFolder; private readonly bool _indexExists = false; private readonly bool _enableIndexInMemoryCachee; private readonly Func _keyDeserializer; private readonly TMeta _meta; private readonly PhisicalFileAccessorCachee _phisicalFileAccessorCachee; public StorePartitionSparseIndex(string partitionFolder, TMeta meta, StoreFilePartition filePartition, Func keyComparer, bool enableIndexInMemoryCachee, PhisicalFileAccessorCachee phisicalFileAccessorCachee) { _indexFolder = Path.Combine(partitionFolder, "__indexes__"); _indexExists = Directory.Exists(_indexFolder); _meta = meta; _keyComparer = keyComparer; _filePartition = filePartition; _keyDeserializer = MessageSerializer.GetDeserializer(); _enableIndexInMemoryCachee = enableIndexInMemoryCachee; if (_enableIndexInMemoryCachee) { _phisicalFileAccessorCachee = phisicalFileAccessorCachee; } } public KeyIndex GetOffset(TKey key) { if (_indexExists) { var index = GetFileIndex(key); int pos = 0; if (index != null && index.Length > 0) { return BinarySearchInIndex(index, key, ref pos); } } return new KeyIndex { Key = key, Offset = 0 }; } public KeyIndex[] GetOffset(TKey[] keys, bool inOneGroup) { var result = new KeyIndex[keys.Length]; int position = 0; if (inOneGroup) { var index = GetFileIndex(keys[0]); for (int i = 0; i < keys.Length; i++) { result[i] = BinarySearchInIndex(index, keys[i], ref position); } } else { for (int i = 0; i < keys.Length; i++) { var index = GetFileIndex(keys[i]); result[i] = BinarySearchInIndex(index, keys[i], ref position); } } return result; } private KeyIndex BinarySearchInIndex(KeyIndex[] index, TKey key, ref int position) { if (index == null || index.Length == 0) { return new KeyIndex { Key = key, Offset = 0 }; } int left = position; int right = index.Length - 1; int mid = 0; TKey test; while (left <= right) { mid = (int)Math.Floor((right + left) / 2d); test = index[mid].Key; var c = _keyComparer(test, key); if (c < 0) { left = mid + 1; } else if (c > 0) { right = mid - 1; } else { break; } } position = mid; while (_keyComparer(index[position].Key, key) > 0 && position > 0) position--; return index[position]; } public void ResetCachee() { if (_enableIndexInMemoryCachee) { _phisicalFileAccessorCachee.DropAllIndexReaders(); } } public void RemoveCacheeItem(string name) { var file = Path.Combine(_indexFolder, name); if (_enableIndexInMemoryCachee) { _phisicalFileAccessorCachee.DropIndexReader(file); } } private KeyIndex[] GetFileIndex(TKey key) { var indexName = _filePartition.FileNameExtractor.Invoke(key, _meta); var filePath = Path.Combine(_indexFolder, indexName); try { return ReadIndexesFromIndexFile(filePath); } catch (Exception ex) { Log.SystemError(ex, "[StorePartitionSparseIndex.GetFileIndex] No cachee"); } return null; } private KeyIndex[] ReadIndexesFromIndexFile(string filePath) { if (File.Exists(filePath)) { var list = new List>(); var accessor = _enableIndexInMemoryCachee ? _phisicalFileAccessorCachee.GetIndexAccessor(filePath, 0) : new StreamVewAccessor(new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.None)); using (var reader = new MemoryStreamReader(accessor)) { var index = new KeyIndex(); if (reader.EOS == false) { index.Key = _keyDeserializer.Invoke(reader); } if (reader.EOS == false) { index.Offset = reader.ReadLong(); } while (reader.EOS == false) { var k = _keyDeserializer.Invoke(reader); var o = reader.ReadLong(); index.Length = (int)(o - index.Offset); list.Add(index); index = new KeyIndex { Key = k, Offset = o }; } if (index.Offset > 0) { index.Length = 0; list.Add(index); } } return list.ToArray(); } return null; } } }