///<summary>DBから読み込んだハッシュをそのままファイルに書き出す</summary> ///<param name="SaveTime">保存するファイル名に付けるUNIX時刻</param> public async Task <long> AllMediaHash(long SaveTime) { try { long TotalHashCount = 0; string HashFilePath = HashFile.AllHashFilePathBase(SaveTime.ToString()); using (var writer = new BufferedLongWriter(HashFile.TempFilePath(HashFilePath))) { var LoadHashBlock = new TransformBlock <long, AddOnlyList <long> >(async(i) => { var table = new AddOnlyList <long>(TableListSize); while (true) { using var cmd = new MySqlCommand(@"SELECT DISTINCT dcthash FROM media WHERE dcthash BETWEEN @begin AND @end GROUP BY dcthash;"); cmd.Parameters.Add("@begin", MySqlDbType.Int64).Value = i << HashUnitBits; cmd.Parameters.Add("@end", MySqlDbType.Int64).Value = ((i + 1) << HashUnitBits) - 1; if (await ExecuteReader(cmd, (r) => table.Add(r.GetInt64(0)), IsolationLevel.ReadUncommitted).ConfigureAwait(false)) { break; } else { table.Clear(); } } return(table); }, new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount, BoundedCapacity = Environment.ProcessorCount << 1, SingleProducerConstrained = true }); var WriterBlock = new ActionBlock <AddOnlyList <long> >(async(table) => { await writer.Write(table.InnerArray, table.Count).ConfigureAwait(false); TotalHashCount += table.Count; table.Dispose(); }, new ExecutionDataflowBlockOptions() { MaxDegreeOfParallelism = 1 }); LoadHashBlock.LinkTo(WriterBlock, new DataflowLinkOptions() { PropagateCompletion = true }); for (int i = 0; i < 1 << (64 - HashUnitBits); i++) { await LoadHashBlock.SendAsync(i).ConfigureAwait(false); } LoadHashBlock.Complete(); await WriterBlock.Completion.ConfigureAwait(false); } File.Move(HashFile.TempFilePath(HashFilePath), HashFilePath); return(TotalHashCount); } catch (Exception e) { Console.WriteLine(e); return(-1); } }
///<summary>ブロックソートで一致する範囲ごとに読み出す ///長さ2以上のやつは全部返す(NewHashが含まれてなくても返す) ///最後まで読み終わったらnull</summary> ///<returns>(要素数,実際の要素…)を繰り返し、要素数0で終わりを示すオレオレ配列</returns> public AddOnlyList <long> ReadBlocks() { var ReadBlockList = new AddOnlyList <long>(ReadBlockListSize); //ブロックソートで一致してる要素数 int BlockCount = 0; //↑の値を入れるReadBlocks内の添字 int BlockCountIndex = 0; //最初は絶対に一致させないように-1 long MaskedKey = -1; //重複排除用 //long PreviousValue = LastValue; int ValueIndex = LastIndex; while (0 < SortedMemory.Length) { //SortedMemoryはActualRead()毎に変わるのでここでSpanにする var SortedValues = SortedMemory.Span; for (; ValueIndex < SortedValues.Length; ValueIndex++) { long Value = SortedValues[ValueIndex]; long MaskedValue = Value & SortMask; if (MaskedKey == MaskedValue) { //Maskしたやつ同士が一致するなら普通に続きに加える ReadBlockList.Add(Value); BlockCount++; } else { //Maskしたやつが一致しない場合はオレオレ配列1サイクル分終了 MaskedKey = MaskedValue; //2要素以上あれば確定して次のサイクルに進む if (1 < BlockCount) { ReadBlockList.InnerArray[BlockCountIndex] = BlockCount; BlockCountIndex = ReadBlockList.Count; //とりあえず次のサイクルの要素数のダミーを入れる ReadBlockList.Add(0); //十分な要素数が入ってたらここで終了(↑のAdd(0)は終端を示す0になる) if (BlockElementsMin < ReadBlockList.Count) { //SortedValues[ValueIndex]の値を使わなかったので次回に回す LastIndex = ValueIndex; return(ReadBlockList); } ReadBlockList.Add(Value); BlockCount = 1; } //1要素しかないものは無意味なので除外→次の要素で上書き else if (BlockCount == 1) { ReadBlockList.ReplaceTail(Value); } //最初だけ必ずここに入る else { //先頭の要素数のダミーを入れる ReadBlockList.Add(0); ReadBlockList.Add(Value); BlockCount = 1; } } //PreviousValue = Value; } ActualRead(); ValueIndex = 0; } //ここに来るのはファイルの読み込みが終わったときだけ //読み込み終了後の呼び出しならnullを返す必要がある if (ReadBlockList.Count == 0) { ReadBlockList.Dispose(); return(null); } else { //終端に0をつけて完成 ReadBlockList.Add(0); return(ReadBlockList); } }