Skip to content

Instantly share code, notes, and snippets.

@ayende
Last active June 13, 2017 23:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ayende/23853ab31706b334982ead1318efff1a to your computer and use it in GitHub Desktop.
Save ayende/23853ab31706b334982ead1318efff1a to your computer and use it in GitHub Desktop.
using System;
using System.Diagnostics;
using System.IO;
using System.IO.MemoryMappedFiles;
namespace FileAnalyzer
{
public static unsafe class NativeRecord
{
private static readonly int[] DaysToMonth365 =
{
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
};
private static readonly int[] DaysToMonth366 =
{
0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366
};
public static void Parse(byte* buffer, out int id, out int duration)
{
duration = DiffTimesInSecond(buffer + 20, buffer);
id = ParseInt(buffer + 40, 8);
}
private static int DiffTimesInSecond(byte* start, byte* end)
{
if ((*(long*) start == *(long*) end) &&
(*(int*) (start + sizeof(long)) == *(int*) (end + sizeof(long))))
{
// same day, just compare the times
var startTime = ParseInt(start + 11, 2)*3600 + ParseInt(start + 14, 2)*60 + ParseInt(start + 17, 2);
var endTime = ParseInt(end + 11, 2)*3600 + ParseInt(end + 14, 2)*60 + ParseInt(end + 17, 2);
return endTime - startTime;
}
return UnlikelyFullDateDiff(start, end);
}
private static int UnlikelyFullDateDiff(byte* start, byte* end)
{
return ParseDateInSeconds(end) - ParseDateInSeconds(start);
}
private static int ParseDateInSeconds(byte* buffer)
{
var year = ParseInt(buffer, 4);
var month = ParseInt(buffer + 5, 2);
var day = ParseInt(buffer + 8, 2);
var hour = ParseInt(buffer + 11, 2);
var min = ParseInt(buffer + 14, 2);
var sec = ParseInt(buffer + 17, 2);
var leap = (year%4 == 0) && ((year%100 != 0) || (year%400 == 0));
var days = leap ? DaysToMonth366 : DaysToMonth365;
var y = year - 1;
var n = y*365 + y/4 - y/100 + y/400 + days[month - 1] + day - 1;
var totalSeconds = hour*3600 + min*60 + sec;
return n*24*60*60 + totalSeconds;
}
private static int ParseInt(byte* buffer, int size)
{
var val = 0;
for (var i = 0; i < size; i++)
{
val *= 10;
val += buffer[i] - '0';
}
return val;
}
}
internal unsafe class Program
{
private static void Increment(ref int[] array, int id, int value)
{
if (id < array.Length)
{
array[id] += value;
return;
}
UnlikelyGrowArray(ref array, id, value);
}
private static void UnlikelyGrowArray(ref int[] array, int id, int value)
{
var size = array.Length*2;
while (id >= size)
size *= 2;
Array.Resize(ref array, size);
Increment(ref array, id, value);
}
private static void Main(string[] args)
{
AppDomain.MonitoringIsEnabled = true;
var sp = Stopwatch.StartNew();
var stats = new int[256];
using (var mmf = MemoryMappedFile.CreateFromFile(args[0]))
{
using (var accessor = mmf.CreateViewAccessor())
{
byte* buffer = null;
accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref buffer);
var len = new FileInfo(args[0]).Length;
var entries = len/50;
for (var i = 0; i < entries; i++)
{
int id;
int duration;
NativeRecord.Parse(buffer, out id, out duration);
buffer += 50;
Increment(ref stats, id, duration);
}
}
}
WriteOutput(stats);
Console.WriteLine($"Took: {sp.ElapsedMilliseconds:#,#} ms and allocated " +
$"{AppDomain.CurrentDomain.MonitoringTotalAllocatedMemorySize/1024:#,#} kb " +
$"with peak working set of {Process.GetCurrentProcess().PeakWorkingSet64/1024:#,#} kb");
}
private static void WriteOutput(int[] stats)
{
var tempBuffer = new byte[21];
tempBuffer[10] = (byte) ' ';
tempBuffer[13] = (byte) ':';
tempBuffer[16] = (byte) ':';
tempBuffer[19] = (byte) '\r';
tempBuffer[20] = (byte) '\n';
using (var output = File.Create("summary.txt"))
{
for (var i = 0; i < stats.Length; i++)
{
var value = stats[i];
if (value == 0)
continue;
WriteFormattedInt(i, tempBuffer, 0, 10);
var ts = TimeSpan.FromSeconds(value);
WriteFormattedTime(ts, tempBuffer, 11);
output.Write(tempBuffer, 0, tempBuffer.Length);
}
}
}
private static void WriteFormattedInt(int id, byte[] temp, int pos, int numberOfDigits)
{
var i = pos + numberOfDigits - 1;
do
{
temp[i--] = (byte) (id%10 + '0');
} while ((id /= 10) > 0);
while (i >= pos)
temp[i--] = (byte) '0';
}
private static void WriteFormattedTime(TimeSpan ts, byte[] temp, int pos)
{
var hours = ts.Hours;
temp[pos] = (byte) (hours/10 + '0');
temp[pos + 1] = (byte) (hours%10 + '0');
var min = ts.Minutes;
temp[pos + 3] = (byte) (min/10 + '0');
temp[pos + 4] = (byte) (min%10 + '0');
var sec = ts.Seconds;
temp[pos + 5] = (byte) (sec/10 + '0');
temp[pos + 6] = (byte) (sec%10 + '0');
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment