Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new SerializeAs option for TerminatedSizedString. #214

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions BinarySerializer.Test/SerializeAs/SerializeAsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,24 @@ public void CollectionPaddingValue()
var actualItems = actual.Items.Select(i => i.Trim()).ToList();
CollectionAssert.AreEqual(expected.Items, actualItems);
}

[TestMethod]
public void SerializeAsTerminatedStringWithPadding()
{
var expected = new TerminatedSizedStringClass { Value = "hi" };
var actual = Roundtrip(expected, new byte[] { 0x68, 0x69, 0x0A, 0x0D, 0x0D });

Assert.AreEqual(expected.Value, actual.Value);
}

[TestMethod]
public void SerializeAsTerminatedStringWithTruncation()
{
var expected = new TerminatedSizedStringClass { Value = "hi test" };
var actual = Roundtrip(expected, new byte[] { 0x68, 0x69, 0x20, 0x74, 0x0A });

// we expect to have truncated to only have 4 characters (and the terminator)
Assert.AreEqual(expected.Value.Substring(0,4), actual.Value);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace BinarySerialization.Test.SerializeAs
{
class TerminatedSizedStringClass
{
[FieldLength(5)]
[SerializeAs(SerializedType.TerminatedSizedString, StringTerminator = '\n', PaddingValue = 0x0D)]
public string Value { get; set; }
}
}
3 changes: 2 additions & 1 deletion BinarySerializer/Graph/TypeGraph/EnumTypeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ private void InitializeEnumValues()
if (enumAttributes.Any(enumAttribute => enumAttribute.Value != null) ||
serializedType == SerializedType.TerminatedString ||
serializedType == SerializedType.SizedString ||
serializedType == SerializedType.LengthPrefixedString)
serializedType == SerializedType.LengthPrefixedString ||
serializedType == SerializedType.TerminatedSizedString)
{
EnumInfo.EnumValues = enumAttributes.ToDictionary(enumAttribute => enumAttribute.Key,
enumAttribute =>
Expand Down
7 changes: 5 additions & 2 deletions BinarySerializer/Graph/TypeGraph/TypeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ internal abstract class TypeNode : Node<TypeNode>
{SerializedType.TerminatedString, default(string)},
{SerializedType.SizedString, default(string)},
{SerializedType.LengthPrefixedString, default(string)},
{SerializedType.TerminatedSizedString, default(string)},
{SerializedType.ByteArray, default(byte[])}
};

Expand Down Expand Up @@ -154,7 +155,8 @@ protected TypeNode(TypeNode parent, Type parentType, MemberInfo memberInfo, Type
}
#pragma warning restore 618

if (_serializedType.Value == SerializedType.TerminatedString)
if (_serializedType.Value == SerializedType.TerminatedString ||
_serializedType.Value == SerializedType.TerminatedSizedString)
{
AreStringsTerminated = true;
StringTerminator = serializeAsAttribute.StringTerminator;
Expand All @@ -170,7 +172,8 @@ protected TypeNode(TypeNode parent, Type parentType, MemberInfo memberInfo, Type
IsNullable = serializedType == SerializedType.Default ||
serializedType == SerializedType.ByteArray ||
serializedType == SerializedType.TerminatedString ||
serializedType == SerializedType.SizedString;
serializedType == SerializedType.SizedString ||
serializedType == SerializedType.TerminatedSizedString;
}

// setup bindings
Expand Down
98 changes: 75 additions & 23 deletions BinarySerializer/Graph/ValueGraph/ValueValueNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,16 +133,19 @@ public void Serialize(AsyncBinaryWriter writer, object value, SerializedType ser

// see if we're dealing with something that needs to be padded
if (serializedType != SerializedType.ByteArray &&
serializedType != SerializedType.TerminatedString)
serializedType != SerializedType.TerminatedString &&
serializedType != SerializedType.TerminatedSizedString)
{
return;
}

var data = new byte[constLength.TotalByteCount];

if (serializedType == SerializedType.TerminatedString && data.Length > 0)
if ((serializedType == SerializedType.TerminatedString ||
serializedType == SerializedType.TerminatedSizedString)
&& data.Length > 0)
{
var terminatorData = GetNullTermination();
var terminatorData = GetTermination();
Array.Copy(terminatorData, data, terminatorData.Length);
}

Expand Down Expand Up @@ -224,7 +227,7 @@ public void Serialize(AsyncBinaryWriter writer, object value, SerializedType ser
var encoding = GetFieldEncoding();
var data = encoding.GetBytes(value.ToString());

var dataLength = GetNullTerminatedArrayLength(data, constLength, maxLength);
var dataLength = GetTerminatedArrayLength(data, constLength, maxLength);

writer.Write(data, dataLength);

Expand All @@ -234,7 +237,8 @@ public void Serialize(AsyncBinaryWriter writer, object value, SerializedType ser
}
case SerializedType.SizedString:
{
var data = GetFieldEncoding().GetBytes(value.ToString());
var encoding = GetFieldEncoding();
var data = encoding.GetBytes(value.ToString());

var dataLength = GetArrayLength(data, constLength, maxLength);

Expand All @@ -251,6 +255,25 @@ public void Serialize(AsyncBinaryWriter writer, object value, SerializedType ser
writer.Write(value.ToString());
break;
}
case SerializedType.TerminatedSizedString:
{
var encoding = GetFieldEncoding();
var data = encoding.GetBytes(value.ToString());

var dataLength = GetTerminatedArrayLength(data, constLength, maxLength);

writer.Write(data, dataLength);

var stringTerminatorData = encoding.GetBytes(new[] { TypeNode.StringTerminator });
writer.Write(stringTerminatorData, stringTerminatorData.Length);

var fillLength = maxLength < constLength ? maxLength : constLength;
var padLength = fillLength - (dataLength + stringTerminatorData.Length);
if (padLength > 0)
writer.Write(GetFieldPaddingValue(), padLength);

break;
}

default:
throw new NotSupportedException(TypeNotSupportedMessage);
Expand Down Expand Up @@ -286,16 +309,19 @@ public async Task SerializeAsync(AsyncBinaryWriter writer, object value, Seriali

// see if we're dealing with something that needs to be padded
if (serializedType != SerializedType.ByteArray &&
serializedType != SerializedType.TerminatedString)
serializedType != SerializedType.TerminatedString &&
serializedType != SerializedType.TerminatedSizedString)
{
return;
}

var data = new byte[constLength.TotalByteCount];

if (serializedType == SerializedType.TerminatedString && data.Length > 0)
if ((serializedType == SerializedType.TerminatedString ||
serializedType == SerializedType.TerminatedSizedString)
&& data.Length > 0)
{
var terminatorData = GetNullTermination();
var terminatorData = GetTermination();
Array.Copy(terminatorData, data, terminatorData.Length);
}

Expand Down Expand Up @@ -376,11 +402,11 @@ public async Task SerializeAsync(AsyncBinaryWriter writer, object value, Seriali
{
var data = GetFieldEncoding().GetBytes(value.ToString());

var dataLength = GetNullTerminatedArrayLength(data, constLength, maxLength);
var dataLength = GetTerminatedArrayLength(data, constLength, maxLength);

await writer.WriteAsync(data, dataLength, cancellationToken).ConfigureAwait(false);

var stringTerminatorData = GetNullTermination();
var stringTerminatorData = GetTermination();
await writer.WriteAsync(TypeNode.StringTerminator, stringTerminatorData.Length, cancellationToken)
.ConfigureAwait(false);

Expand All @@ -404,6 +430,25 @@ await writer.WriteAsync(TypeNode.StringTerminator, stringTerminatorData.Length,
writer.Write(value.ToString());
break;
}
case SerializedType.TerminatedSizedString:
{
var encoding = GetFieldEncoding();
var data = encoding.GetBytes(value.ToString());

var dataLength = GetTerminatedArrayLength(data, constLength, maxLength);

await writer.WriteAsync(data, dataLength, cancellationToken);

var stringTerminatorData = encoding.GetBytes(new[] { TypeNode.StringTerminator });
await writer.WriteAsync(stringTerminatorData, stringTerminatorData.Length, cancellationToken);

var fillLength = maxLength < constLength ? maxLength : constLength;
var padLength = fillLength - (dataLength + stringTerminatorData.Length);
if (padLength > 0)
await writer.WriteAsync(GetFieldPaddingValue(), padLength, cancellationToken);

break;
}

default:

Expand Down Expand Up @@ -467,7 +512,8 @@ public void Deserialize(BinaryReader reader, SerializedType serializedType, Fiel
break;
}
case SerializedType.TerminatedString:
{
case SerializedType.TerminatedSizedString:
{
var data = ReadTerminated(reader, effectiveLengthValue, TypeNode.StringTerminator);
value = GetFieldEncoding().GetString(data, 0, data.Length);
break;
Expand Down Expand Up @@ -540,7 +586,8 @@ public async Task DeserializeAsync(AsyncBinaryReader reader, SerializedType seri
break;
}
case SerializedType.TerminatedString:
{
case SerializedType.TerminatedSizedString:
{
var data = await ReadTerminatedAsync(reader, (int) effectiveLengthValue.ByteCount, TypeNode.StringTerminator,
cancellationToken)
.ConfigureAwait(false);
Expand Down Expand Up @@ -675,7 +722,7 @@ private FieldLength GetConstLength(FieldLength length)
return constLength;
}

private byte[] GetNullTermination()
private byte[] GetTermination()
{
return GetFieldEncoding().GetBytes(new[] { TypeNode.StringTerminator });
}
Expand All @@ -684,8 +731,10 @@ private static FieldLength GetMaxLength(BoundedStream stream, SerializedType ser
{
FieldLength maxLength = null;

if (serializedType == SerializedType.ByteArray || serializedType == SerializedType.SizedString ||
serializedType == SerializedType.TerminatedString)
if (serializedType == SerializedType.ByteArray ||
serializedType == SerializedType.SizedString ||
serializedType == SerializedType.TerminatedString ||
serializedType == SerializedType.TerminatedSizedString)
{
// try to get bounded length
maxLength = stream.AvailableForWriting;
Expand All @@ -711,20 +760,22 @@ private static FieldLength GetArrayLength(byte[] data, FieldLength constLength,
return length;
}

private FieldLength GetNullTerminatedArrayLength(byte[] data, FieldLength constLength, FieldLength maxLength)
private FieldLength GetTerminatedArrayLength(byte[] data, FieldLength constLength, FieldLength maxLength)
{
FieldLength length = data.Length;
var nullTermination = GetNullTermination();
var nullTerminationLength = new FieldLength(nullTermination.Length);
var termination = GetTermination();
var terminationLength = new FieldLength(termination.Length);

if (constLength != null)
if (constLength != null &&
constLength < length + terminationLength)
{
length = constLength - nullTerminationLength;
length = constLength - terminationLength;
}

if (maxLength != null && length > maxLength)
if (maxLength != null &&
maxLength < length + terminationLength)
{
length = maxLength - nullTerminationLength;
length = maxLength - terminationLength;
}

return length;
Expand Down Expand Up @@ -776,7 +827,8 @@ private FieldLength GetEffectiveLengthValue(BinaryReader reader, SerializedType
{
if (serializedType == SerializedType.ByteArray ||
serializedType == SerializedType.SizedString ||
serializedType == SerializedType.TerminatedString)
serializedType == SerializedType.TerminatedString ||
serializedType == SerializedType.TerminatedSizedString)
{
// try to get bounded length
var baseStream = (BoundedStream) reader.BaseStream;
Expand Down
8 changes: 7 additions & 1 deletion BinarySerializer/SerializedType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ public enum SerializedType
/// <summary>
/// An encoded string prefixed with a LEB128-encoded length. This is equivalent to how BinaryWriter encodes a string.
/// </summary>
LengthPrefixedString
LengthPrefixedString,

/// <summary>
/// An encoded string with a terminator, which is null (zero) by default or can be specified by setting StringTerminator.
/// Also has a maximum length storage available
/// </summary>
TerminatedSizedString,
}
}