aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/embeddable-dll-service/csharp/Ringlogger.cs
diff options
context:
space:
mode:
Diffstat (limited to 'embeddable-dll-service/csharp/Ringlogger.cs')
-rw-r--r--embeddable-dll-service/csharp/Ringlogger.cs190
1 files changed, 171 insertions, 19 deletions
diff --git a/embeddable-dll-service/csharp/Ringlogger.cs b/embeddable-dll-service/csharp/Ringlogger.cs
index 711c22e7..c265142c 100644
--- a/embeddable-dll-service/csharp/Ringlogger.cs
+++ b/embeddable-dll-service/csharp/Ringlogger.cs
@@ -7,40 +7,192 @@ using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Text;
+using System.Collections.Generic;
+using System.Threading;
+using System.Runtime.CompilerServices;
namespace Tunnel
{
public class Ringlogger
{
- private readonly MemoryMappedViewAccessor _viewAccessor;
+ private struct UnixTimestamp
+ {
+ private Int64 _ns;
+ public UnixTimestamp(Int64 ns) => _ns = ns;
+ public bool IsEmpty => _ns == 0;
+ public static UnixTimestamp Empty => new UnixTimestamp(0);
+ public static UnixTimestamp Now
+ {
+ get
+ {
+ var now = DateTimeOffset.UtcNow;
+ var ns = (now.Subtract(DateTimeOffset.FromUnixTimeSeconds(0)).Ticks * 100) % 1000000000;
+ return new UnixTimestamp(now.ToUnixTimeSeconds() * 1000000000 + ns);
+ }
+ }
+ public Int64 Nanoseconds => _ns;
+ public override string ToString()
+ {
+ return DateTimeOffset.FromUnixTimeSeconds(_ns / 1000000000).LocalDateTime.ToString("yyyy'-'MM'-'dd HH':'mm':'ss'.'") + ((_ns % 1000000000).ToString() + "00000").Substring(0, 6);
+ }
+ }
+ private struct Line
+ {
+ private const int maxLineLength = 512;
+ private const int offsetTimeNs = 0;
+ private const int offsetLine = 8;
+
+ private readonly MemoryMappedViewAccessor _view;
+ private readonly int _start;
+ public Line(MemoryMappedViewAccessor view, UInt32 index) => (_view, _start) = (view, (int)(Log.HeaderBytes + index * Bytes));
+
+ public static int Bytes => maxLineLength + offsetLine;
+
+ public UnixTimestamp Timestamp
+ {
+ get => new UnixTimestamp(_view.ReadInt64(_start + offsetTimeNs));
+ set => _view.Write(_start + offsetTimeNs, value.Nanoseconds);
+ }
+
+ public string Text
+ {
+ get
+ {
+ var textBytes = new byte[maxLineLength];
+ _view.ReadArray(_start + offsetLine, textBytes, 0, textBytes.Length);
+ var nullByte = Array.IndexOf<byte>(textBytes, 0);
+ if (nullByte <= 0)
+ return null;
+ return Encoding.UTF8.GetString(textBytes, 0, nullByte);
+ }
+ set
+ {
+ if (value == null)
+ {
+ _view.WriteArray(_start + offsetLine, new byte[maxLineLength], 0, maxLineLength);
+ return;
+ }
+ var textBytes = Encoding.UTF8.GetBytes(value);
+ var bytesToWrite = Math.Min(maxLineLength - 1, textBytes.Length);
+ _view.Write(_start + offsetLine + bytesToWrite, (byte)0);
+ _view.WriteArray(_start + offsetLine, textBytes, 0, bytesToWrite);
+ }
+ }
- public Ringlogger(string filename)
+ public override string ToString()
+ {
+ var time = Timestamp;
+ if (time.IsEmpty)
+ return null;
+ var text = Text;
+ if (text == null)
+ return null;
+ return string.Format("{0}: {1}", time, text);
+ }
+ }
+ private struct Log
{
- var file = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
- var mmap = MemoryMappedFile.CreateFromFile(file, null, 0, MemoryMappedFileAccess.Read, HandleInheritability.None, false);
- _viewAccessor = mmap.CreateViewAccessor(0, 8 + 2048 * (512 + 8), MemoryMappedFileAccess.Read);
- if (_viewAccessor.ReadUInt32(0) != 0xbadbabe)
- throw new InvalidDataException("The provided file is missing the magic number.");
+ private const UInt32 maxLines = 2048;
+ private const UInt32 magic = 0xbadbabe;
+ private const int offsetMagic = 0;
+ private const int offsetNextIndex = 4;
+ private const int offsetLines = 8;
+
+ private readonly MemoryMappedViewAccessor _view;
+ public Log(MemoryMappedViewAccessor view) => _view = view;
+
+ public static int HeaderBytes => offsetLines;
+ public static int Bytes => (int)(HeaderBytes + Line.Bytes * maxLines);
+
+ public UInt32 ExpectedMagic => magic;
+ public UInt32 Magic
+ {
+ get => _view.ReadUInt32(offsetMagic);
+ set => _view.Write(offsetMagic, value);
+ }
+
+ public UInt32 NextIndex
+ {
+ get => _view.ReadUInt32(offsetNextIndex);
+ set => _view.Write(offsetNextIndex, value);
+ }
+ public unsafe UInt32 InsertNextIndex() => (UInt32)Interlocked.Increment(ref Unsafe.AsRef<Int32>((_view.SafeMemoryMappedViewHandle.DangerousGetHandle() + offsetNextIndex).ToPointer()));
+
+ public UInt32 LineCount => maxLines;
+ public Line this[UInt32 i] => new Line(_view, i % maxLines);
+
+ public void Clear() => _view.WriteArray(0, new byte[Log.Bytes], 0, Log.Bytes);
+ }
+
+ private readonly Log _log;
+ private readonly string _tag;
+
+ public Ringlogger(string filename, string tag)
+ {
+ var file = File.Open(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Delete);
+ file.SetLength(Log.Bytes);
+ var mmap = MemoryMappedFile.CreateFromFile(file, null, 0, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, false);
+ var view = mmap.CreateViewAccessor(0, Log.Bytes, MemoryMappedFileAccess.ReadWrite);
+ _log = new Log(view);
+ if (_log.Magic != _log.ExpectedMagic)
+ {
+ _log.Clear();
+ _log.Magic = _log.ExpectedMagic;
+ }
+ _tag = tag;
+ }
+
+ public void Write(string line)
+ {
+ var time = UnixTimestamp.Now;
+ var entry = _log[_log.InsertNextIndex() - 1];
+ entry.Timestamp = UnixTimestamp.Empty;
+ entry.Text = null;
+ entry.Text = string.Format("[{0}] {1}", _tag, line.Trim());
+ entry.Timestamp = time;
}
public void WriteTo(TextWriter writer)
{
- var start = _viewAccessor.ReadUInt32(4);
- for (var i = 0; i < 2048; ++i)
+ var start = _log.NextIndex;
+ for (UInt32 i = 0; i < _log.LineCount; ++i)
{
- var lineOffset = 8 + (8 + 512) * ((i + start) % 2048);
- var timeNs = _viewAccessor.ReadInt64(lineOffset);
- if (timeNs == 0)
+ var entry = _log[i + start];
+ if (entry.Timestamp.IsEmpty)
+ continue;
+ var text = entry.ToString();
+ if (text == null)
continue;
- var textBytes = new byte[512];
- _viewAccessor.ReadArray<byte>(lineOffset + 8, textBytes, 0, textBytes.Length);
- var nullByte = Array.IndexOf<byte>(textBytes, 0);
- if (nullByte <= 0)
+ writer.WriteLine(text);
+ }
+ }
+
+ public static readonly UInt32 CursorAll = UInt32.MaxValue;
+ public List<string> FollowFromCursor(ref UInt32 cursor)
+ {
+ var lines = new List<string>((int)_log.LineCount);
+ var i = cursor;
+ var all = cursor == CursorAll;
+ if (all)
+ i = _log.NextIndex;
+ for (UInt32 l = 0; l < _log.LineCount; ++l, ++i)
+ {
+ if (!all && i % _log.LineCount == _log.NextIndex % _log.LineCount)
+ break;
+ var entry = _log[i];
+ if (entry.Timestamp.IsEmpty)
+ {
+ if (all)
+ continue;
+ break;
+ }
+ cursor = (i + 1) % _log.LineCount;
+ var text = entry.ToString();
+ if (text == null)
continue;
- var text = Encoding.UTF8.GetString(textBytes, 0, nullByte);
- var time = DateTimeOffset.FromUnixTimeMilliseconds(timeNs / 1000000).ToString("yyyy'-'MM'-'dd HH':'mm':'ss'.'ffffff");
- writer.WriteLine(String.Format("{0}: {1}", time, text));
+ lines.Add(text);
}
+ return lines;
}
}
}