Fix deserialization of UTF-8 string for C# object properties

Closes #320
This commit is contained in:
Wojtek Figat
2021-03-12 13:49:45 +01:00
parent 4d85a5ac3d
commit a2be4bb74a
3 changed files with 166 additions and 8 deletions

View File

@@ -165,17 +165,19 @@ namespace FlaxEngine.Json
public StringWriter StringWriter;
public JsonTextWriter JsonWriter;
public JsonSerializerInternalWriter SerializerWriter;
public UnmanagedStringReader StringReader;
public UnmanagedMemoryStream MemoryStream;
public StreamReader Reader;
public bool IsDuringSerialization;
public SerializerCache(JsonSerializerSettings settings)
public unsafe SerializerCache(JsonSerializerSettings settings)
{
JsonSerializer = Newtonsoft.Json.JsonSerializer.CreateDefault(settings);
JsonSerializer.Formatting = Formatting.Indented;
StringBuilder = new StringBuilder(256);
StringWriter = new StringWriter(StringBuilder, CultureInfo.InvariantCulture);
SerializerWriter = new JsonSerializerInternalWriter(JsonSerializer);
StringReader = new UnmanagedStringReader();
MemoryStream = new UnmanagedMemoryStream((byte*)0, 0);
Reader = new StreamReader(MemoryStream, Encoding.UTF8, false);
JsonWriter = new JsonTextWriter(StringWriter)
{
IndentChar = '\t',
@@ -404,14 +406,15 @@ namespace FlaxEngine.Json
/// <param name="input">The object.</param>
/// <param name="jsonBuffer">The input json data buffer (raw, fixed memory buffer).</param>
/// <param name="jsonLength">The input json data buffer length (characters count).</param>
public static unsafe void Deserialize(object input, void* jsonBuffer, int jsonLength)
public static unsafe void Deserialize(object input, byte* jsonBuffer, int jsonLength)
{
var cache = Cache.Value;
cache.IsDuringSerialization = false;
Current.Value = cache;
cache.StringReader.Initialize(jsonBuffer, jsonLength);
var jsonReader = new JsonTextReader(cache.StringReader);
cache.MemoryStream.Initialize(jsonBuffer, jsonLength);
cache.Reader.DiscardBufferedData();
var jsonReader = new JsonTextReader(cache.Reader);
cache.JsonSerializer.Populate(jsonReader, input);
if (!cache.JsonSerializer.CheckAdditionalContent)

View File

@@ -0,0 +1,157 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace FlaxEngine.Json
{
/// <summary>
/// Implements a <see cref="T:System.IO.Stream" /> that reads from unmanaged buffer (provided as raw pointer and length).
/// </summary>
internal class UnmanagedMemoryStream : Stream
{
private unsafe byte* _ptr;
private int _length;
private int _pos;
private FileAccess _access;
internal bool _isOpen;
private Task<int> _lastReadTask;
internal UnmanagedMemoryStream()
{
}
internal unsafe UnmanagedMemoryStream(byte* pointer, int length, FileAccess access = FileAccess.Read) => Initialize(pointer, length, access);
internal unsafe void Initialize(byte* pointer, int length, FileAccess access = FileAccess.Read)
{
_ptr = pointer;
_length = length;
_pos = 0;
_access = access;
_isOpen = true;
_lastReadTask = null;
}
public override bool CanRead => _isOpen && (uint)(_access & FileAccess.Read) > 0U;
public override bool CanSeek => _isOpen;
public override bool CanWrite => _isOpen && (uint)(_access & FileAccess.Write) > 0U;
protected override unsafe void Dispose(bool disposing)
{
_isOpen = false;
_ptr = null;
base.Dispose(disposing);
}
public override void Flush()
{
}
public override Task FlushAsync(CancellationToken cancellationToken)
{
Flush();
return Task.FromResult(0);
}
public unsafe byte* Pointer => _ptr;
public override long Length => _length;
public long Capacity => _length;
public override long Position
{
get => _pos;
set => _pos = (int)value;
}
public unsafe byte* PositionPointer
{
get => _ptr + _pos;
set => _pos = (int)(value - _ptr);
}
public override unsafe int Read(byte[] buffer, int offset, int count)
{
int toRead = _length - _pos;
if (toRead > count)
toRead = count;
if (toRead <= 0)
return 0;
fixed (byte* bufferPtr = buffer)
Utils.MemoryCopy(new IntPtr(_ptr + _pos), new IntPtr(bufferPtr), toRead);
_pos += toRead;
return toRead;
}
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
int result = Read(buffer, offset, count);
return _lastReadTask == null || _lastReadTask.Result != result ? (_lastReadTask = Task.FromResult(result)) : _lastReadTask;
}
public override unsafe int ReadByte()
{
int index = _pos;
if (index >= _length)
return -1;
_pos = index + 1;
return _ptr[index];
}
public override long Seek(long offset, SeekOrigin loc)
{
switch (loc)
{
case SeekOrigin.Begin:
_pos = (int)offset;
break;
case SeekOrigin.Current:
_pos += (int)offset;
break;
case SeekOrigin.End:
_pos = _length + (int)offset;
break;
default: throw new ArgumentOutOfRangeException();
}
return _pos;
}
public override void SetLength(long value)
{
_length = (int)value;
if (_pos > value)
_pos = _length;
}
public override unsafe void Write(byte[] buffer, int offset, int count)
{
int newPos = _pos + count;
if (newPos > _length)
_length = newPos;
fixed (byte* bufferPtr = buffer)
Utils.MemoryCopy(new IntPtr(_pos + _pos), new IntPtr(bufferPtr), count);
_pos = newPos;
}
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
Write(buffer, offset, count);
return Task.FromResult(0);
}
public override unsafe void WriteByte(byte value)
{
long newPos = _pos + 1;
if (_pos >= _length)
_length = (int)newPos;
_ptr[_pos] = value;
_pos = (int)newPos;
}
}
}

View File

@@ -1,7 +1,5 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using FlaxEngine.Assertions;
namespace FlaxEngine.GUI
{
/// <summary>