Skip to content

Commit

Permalink
Merge pull request #41 from lmanners/key-value-ascii
Browse files Browse the repository at this point in the history
Add AppendKeyValueAscii methods
  • Loading branch information
ltrzesniewski authored Dec 3, 2020
2 parents 2bfa319 + 4d149b5 commit a7ff187
Show file tree
Hide file tree
Showing 3 changed files with 233 additions and 4 deletions.
51 changes: 49 additions & 2 deletions src/ZeroLog.Tests/LogEventTests.EdgeCases.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ public void should_truncate_ascii_string_if_buffer_is_not_large_enough()

[Test]
public void should_ignore_ascii_string_if_buffer_is_not_large_enough_for_header(
[Range(_bufferSize - 2 * _asciiHeaderSize, _bufferSize)]
int firstStringLength)
[Range(_bufferSize - 2 * _asciiHeaderSize, _bufferSize)] int firstStringLength)
{
var largeString1 = new string('a', firstStringLength);
var asciiBytes1 = Encoding.ASCII.GetBytes(largeString1);
Expand Down Expand Up @@ -214,6 +213,54 @@ public void should_ignore_append_time_span_if_buffer_is_full()
Check.That(string.IsNullOrWhiteSpace(_output.ToString()));
}

[Test]
public void should_truncate_key_value_ascii_if_too_long_bytes()
{
var largeString = new string('a', 2000);
var bytes = Encoding.ASCII.GetBytes(largeString);
_logEvent.AppendKeyValue("key1", "value1");
_logEvent.AppendKeyValueAscii("key2", bytes, bytes.Length);
_logEvent.WriteToStringBuffer(_output);
Assert.AreEqual(" ~~ { \"key1\": \"value1\" } [TRUNCATED]", _output.ToString());
}

[Test]
public void should_truncate_key_value_ascii_if_too_long_raw_bytes()
{
var largeString = new string('a', 2000);
var asciiBytes = Encoding.ASCII.GetBytes(largeString);
_logEvent.AppendKeyValue("key1", "value1");

fixed (byte* pAsciiBytes = asciiBytes)
{
_logEvent.AppendKeyValueAscii("key2", pAsciiBytes, asciiBytes.Length);
}

_logEvent.WriteToStringBuffer(_output);
Assert.AreEqual(" ~~ { \"key1\": \"value1\" } [TRUNCATED]", _output.ToString());
}

[Test]
public void should_truncate_key_value_ascii_if_too_long_byte_span()
{
var largeString = new string('a', 2000);
var bytes = Encoding.ASCII.GetBytes(largeString);
_logEvent.AppendKeyValue("key1", "value1");
_logEvent.AppendKeyValueAscii("key2", bytes.AsSpan());
_logEvent.WriteToStringBuffer(_output);
Assert.AreEqual(" ~~ { \"key1\": \"value1\" } [TRUNCATED]", _output.ToString());
}

[Test]
public void should_truncate_key_value_ascii_if_too_long_char_span()
{
var largeString = new string('a', 2000);
_logEvent.AppendKeyValue("key1", "value1");
_logEvent.AppendKeyValueAscii("key2", largeString.AsSpan());
_logEvent.WriteToStringBuffer(_output);
Assert.AreEqual(" ~~ { \"key1\": \"value1\" } [TRUNCATED]", _output.ToString());
}

[Test]
public void should_ignore_append_key_values_if_buffer_is_full()
{
Expand Down
106 changes: 104 additions & 2 deletions src/ZeroLog.Tests/LogEventTests.KeyValues.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Text;
using NUnit.Framework;

namespace ZeroLog.Tests
Expand Down Expand Up @@ -27,14 +28,115 @@ public void should_append_multiple_key_values()
[Test]
public void should_append_formatted_string_mixed_with_key_values()
{
// TODO(lmanners): There are more edge cases here.
_logEvent.AppendKeyValue("myKey", "myValue");
_logEvent.AppendFormat("Some {} message");
_logEvent.Append("formatted");
_logEvent.AppendKeyValue("otherKey", 2);
_logEvent.Append("...");
_logEvent.WriteToStringBuffer(_output);

Assert.AreEqual("Some formatted message ~~ { \"myKey\": \"myValue\", \"otherKey\": 2 }", _output.ToString());
Assert.AreEqual("Some formatted message... ~~ { \"myKey\": \"myValue\", \"otherKey\": 2 }", _output.ToString());
}

[Test]
public void should_append_key_byte_array_value()
{
var bytes = Encoding.ASCII.GetBytes("myValue");
_logEvent.AppendKeyValueAscii("myKey", bytes, bytes.Length);
_logEvent.WriteToStringBuffer(_output);

Assert.AreEqual(" ~~ { \"myKey\": \"myValue\" }", _output.ToString());
}

[Test]
public void should_append_key_byte_span_value()
{
var bytes = Encoding.ASCII.GetBytes("myValue");
_logEvent.AppendKeyValueAscii("myKey", bytes.AsSpan());
_logEvent.WriteToStringBuffer(_output);

Assert.AreEqual(" ~~ { \"myKey\": \"myValue\" }", _output.ToString());
}

[Test]
public void should_append_key_char_span_value()
{
_logEvent.AppendKeyValueAscii("myKey", "myValue".AsSpan());
_logEvent.WriteToStringBuffer(_output);

Assert.AreEqual(" ~~ { \"myKey\": \"myValue\" }", _output.ToString());
}

[Test]
public void should_ignore_byte_array_value_with_negative_length()
{
var bytes = Encoding.Default.GetBytes("myValue");
_logEvent.AppendKeyValueAscii("myKey", bytes, -1);
_logEvent.WriteToStringBuffer(_output);

Assert.AreEqual("", _output.ToString());
}

[Test]
public void should_support_byte_array_value_that_is_empty()
{
_logEvent.AppendKeyValueAscii("myKey", new byte[0], 0);
_logEvent.WriteToStringBuffer(_output);

Assert.AreEqual(" ~~ { \"myKey\": \"\" }", _output.ToString());
}

[Test]
public void should_support_null_byte_array()
{
_logEvent.AppendKeyValueAscii("myKey", (byte[])null, 0);
_logEvent.WriteToStringBuffer(_output);

Assert.AreEqual(" ~~ { \"myKey\": null }", _output.ToString());
}

[Test]
public void should_support_empty_byte_pointer()
{
var bytes = new byte[1];
unsafe
{
fixed (byte* pBytes = bytes)
{
_logEvent.AppendKeyValueAscii("myKey", pBytes, 0);
}
}

_logEvent.WriteToStringBuffer(_output);

Assert.AreEqual(" ~~ { \"myKey\": \"\" }", _output.ToString());
}

[Test]
public unsafe void should_support_null_byte_pointer()
{
_logEvent.AppendKeyValueAscii("myKey", (byte*)null, 0);
_logEvent.WriteToStringBuffer(_output);

Assert.AreEqual(" ~~ { \"myKey\": null }", _output.ToString());
}

[Test]
public void should_support_byte_span_value_that_is_empty()
{
_logEvent.AppendKeyValueAscii("myKey", ReadOnlySpan<byte>.Empty);
_logEvent.WriteToStringBuffer(_output);

Assert.AreEqual(" ~~ { \"myKey\": \"\" }", _output.ToString());
}

[Test]
public void should_support_char_span_value_that_is_empty()
{
_logEvent.AppendKeyValueAscii("myKey", ReadOnlySpan<char>.Empty);
_logEvent.WriteToStringBuffer(_output);

Assert.AreEqual(" ~~ { \"myKey\": \"\" }", _output.ToString());
}

[Test]
Expand Down
80 changes: 80 additions & 0 deletions src/ZeroLog/LogEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,86 @@ public ILogEvent AppendKeyValue<T>(string key, T? value)
return this;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ILogEvent AppendKeyValueAscii(string key, byte[]? bytes, int length)
{
if (length < 0 || !PrepareAppend(sizeof(ArgumentType) + sizeof(byte) + sizeof(ArgumentType) + sizeof(int) + length, 2))
return this;

AppendArgumentType(ArgumentType.KeyString);
AppendString(key);

if (bytes is null)
{
AppendArgumentType(ArgumentType.Null);
return this;
}

AppendArgumentType(ArgumentType.AsciiString);
AppendInt32(length);

if (length != 0)
AppendBytes(bytes, length);

return this;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ILogEvent AppendKeyValueAscii(string key, byte* bytes, int length)
{
if (length < 0 || !PrepareAppend(sizeof(ArgumentType) + sizeof(byte) + sizeof(ArgumentType) + sizeof(int) + length, 2))
return this;

AppendArgumentType(ArgumentType.KeyString);
AppendString(key);

if (bytes == null)
{
AppendArgumentType(ArgumentType.Null);
return this;
}

AppendArgumentType(ArgumentType.AsciiString);
AppendInt32(length);
AppendBytes(bytes, length);
return this;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ILogEvent AppendKeyValueAscii(string key, ReadOnlySpan<byte> bytes)
{
var length = bytes.Length;
if (!PrepareAppend(sizeof(ArgumentType) + sizeof(byte) + sizeof(ArgumentType) + sizeof(int) + length, 2))
return this;

AppendArgumentType(ArgumentType.KeyString);
AppendString(key);

AppendArgumentType(ArgumentType.AsciiString);
AppendInt32(length);
AppendBytes(bytes);
return this;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ILogEvent AppendKeyValueAscii(string key, ReadOnlySpan<char> chars)
{
var length = chars.Length;
if (!PrepareAppend(sizeof(ArgumentType) + sizeof(byte) + sizeof(ArgumentType) + sizeof(int) + length, 2))
return this;

AppendArgumentType(ArgumentType.KeyString);
AppendString(key);

AppendArgumentType(ArgumentType.AsciiString);
AppendInt32(length);

foreach (var c in chars)
*_dataPointer++ = (byte)c;

return this;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ILogEvent AppendAsciiString(byte[]? bytes, int length)
{
Expand Down

0 comments on commit a7ff187

Please sign in to comment.