Skip to content

Instantly share code, notes, and snippets.

@barncastle
Created August 8, 2019 18:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save barncastle/bc424b0c8d978e3634acd102032ee939 to your computer and use it in GitHub Desktop.
Save barncastle/bc424b0c8d978e3634acd102032ee939 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using TACT.Net.Common;
using TACT.Net.Cryptography;
namespace TACT.Net.Indices
{
internal class IndexRebuilder
{
private readonly IndexContainer _indexContainer;
private readonly TACTRepo _repo;
private readonly List<FileStream> _handles;
public IndexRebuilder(IndexContainer indexContainer, TACTRepo repo)
{
_indexContainer = indexContainer;
_repo = repo;
_handles = new List<FileStream>();
}
public void Rebuild(string sourcedirectory, string directory)
{
Directory.CreateDirectory(directory);
string tempBlobFilePath = Path.Combine(directory, "temp.blob");
// clear the old archive references from the config
_repo.ConfigContainer.CDNConfig.GetValues("archives").Clear();
_repo.ConfigContainer.CDNConfig.GetValues("archives-index-size").Clear();
var comparer = new MD5HashComparer();
var entries = BuildEntries(sourcedirectory).OrderBy(x => comparer);
var partitions = EnumerablePartitioner.ConcreteBatch(entries, IndexContainer.ArchiveDataSize, (x) => (uint)x.CompressedSize);
foreach (var partition in partitions)
{
// create the blob
WriteTempBlob(partition, tempBlobFilePath);
IndexFile index = new IndexFile(IndexType.Data);
index.Add(partition);
index.Write(directory, _repo.ConfigContainer);
// move the blob to it's real location
string newpath = Helpers.GetCDNPath(index.Checksum.ToString(), "data", directory);
File.Move(tempBlobFilePath, newpath);
}
ClearHandles();
}
/// <summary>
/// Generates a new set of IndexEntries for the new archives
/// </summary>
/// <param name="sourcedirectory"></param>
/// <returns></returns>
private IEnumerable<IndexEntry> BuildEntries(string sourcedirectory)
{
ushort i = 0;
foreach (var index in _indexContainer.DataIndices)
{
if (index.IsLooseIndex || index.IsGroupIndex)
continue;
// open a handle to the existing blob
string blob = Helpers.GetCDNPath(index.Checksum.ToString(), "data", sourcedirectory);
_handles.Add(new FileStream(blob, FileMode.Open, FileAccess.Read, FileShare.Read));
foreach (var entry in index.Entries)
{
// skip unused entries
if (!_repo.EncodingFile.ContainsEKey(entry.Key))
continue;
// create a new entry and set it's handle ref
var tmp = entry.Clone();
tmp.IndexOrdinal = i;
yield return tmp;
}
i++;
}
yield break;
}
/// <summary>
/// Creates the blob in a temporary location
/// </summary>
/// <param name="entries"></param>
/// <param name="filename"></param>
private void WriteTempBlob(IList<IndexEntry> entries, string filename)
{
using (var fs = File.Create(filename))
{
foreach (var entry in entries)
{
// copy the data from the original archive
_handles[entry.IndexOrdinal].Position = entry.Offset;
_handles[entry.IndexOrdinal].PartialCopyTo(fs, (long)entry.CompressedSize);
// reset the handle ref
entry.IndexOrdinal = 0;
}
}
}
/// <summary>
/// Closes all open handles to the blobs
/// </summary>
private void ClearHandles()
{
_handles.ForEach(x => x?.Dispose());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment