Skip to content

Use MessagePackReader \ MessagePackWriter to implement IHubProtocol for server-side Blazor #8687

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

Merged
merged 3 commits into from
Apr 1, 2019
Merged
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
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[submodule "googletest"]
path = src/submodules/googletest
url = https://github.com/google/googletest

[submodule "src/submodules/MessagePack-CSharp"]
path = src/submodules/MessagePack-CSharp
url = https://github.com/aspnet/MessagePack-CSharp.git
4 changes: 3 additions & 1 deletion src/Components/Browser.JS/src/Boot.Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@ async function boot() {
}

async function initializeConnection(circuitHandlers: CircuitHandler[]): Promise<signalR.HubConnection> {
const hubProtocol = new MessagePackHubProtocol();
(hubProtocol as any).name = 'blazorpack';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😻

const connection = new signalR.HubConnectionBuilder()
.withUrl('_blazor')
.withHubProtocol(new MessagePackHubProtocol())
.withHubProtocol(hubProtocol)
.configureLogging(signalR.LogLevel.Information)
.build();

Expand Down
193 changes: 193 additions & 0 deletions src/Components/Server/src/BlazorPack/ArrayBufferWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

// Copied from https://github.com/dotnet/corefx/blob/b0751dcd4a419ba6731dcaa7d240a8a1946c934c/src/System.Text.Json/src/System/Text/Json/Serialization/ArrayBufferWriter.cs

using System;
using System.Buffers;
using System.Diagnostics;

namespace Microsoft.AspNetCore.Components.Server.BlazorPack
{
// Note: this is currently an internal class that will be replaced with a shared version.
internal sealed class ArrayBufferWriter<T> : IBufferWriter<T>, IDisposable
{
private T[] _rentedBuffer;
private int _index;

private const int MinimumBufferSize = 256;

public ArrayBufferWriter()
{
_rentedBuffer = ArrayPool<T>.Shared.Rent(MinimumBufferSize);
_index = 0;
}

public ArrayBufferWriter(int initialCapacity)
{
if (initialCapacity <= 0)
{
throw new ArgumentException(nameof(initialCapacity));
}

_rentedBuffer = ArrayPool<T>.Shared.Rent(initialCapacity);
_index = 0;
}

public ReadOnlyMemory<T> WrittenMemory
{
get
{
CheckIfDisposed();

return _rentedBuffer.AsMemory(0, _index);
}
}

public int WrittenCount
{
get
{
CheckIfDisposed();

return _index;
}
}

public int Capacity
{
get
{
CheckIfDisposed();

return _rentedBuffer.Length;
}
}

public int FreeCapacity
{
get
{
CheckIfDisposed();

return _rentedBuffer.Length - _index;
}
}

public void Clear()
{
CheckIfDisposed();

ClearHelper();
}

private void ClearHelper()
{
Debug.Assert(_rentedBuffer != null);

_rentedBuffer.AsSpan(0, _index).Clear();
_index = 0;
}

// Returns the rented buffer back to the pool
public void Dispose()
{
if (_rentedBuffer == null)
{
return;
}

ClearHelper();
ArrayPool<T>.Shared.Return(_rentedBuffer);
_rentedBuffer = null;
}

private void CheckIfDisposed()
{
if (_rentedBuffer == null)
{
ThrowObjectDisposedException();
}
}

private static void ThrowObjectDisposedException()
{
throw new ObjectDisposedException(nameof(ArrayBufferWriter<T>));
}

public void Advance(int count)
{
CheckIfDisposed();

if (count < 0)
throw new ArgumentException(nameof(count));

if (_index > _rentedBuffer.Length - count)
{
ThrowInvalidOperationException(_rentedBuffer.Length);
}

_index += count;
}

public Memory<T> GetMemory(int sizeHint = 0)
{
CheckIfDisposed();

CheckAndResizeBuffer(sizeHint);
return _rentedBuffer.AsMemory(_index);
}

public Span<T> GetSpan(int sizeHint = 0)
{
CheckIfDisposed();

CheckAndResizeBuffer(sizeHint);
return _rentedBuffer.AsSpan(_index);
}

private void CheckAndResizeBuffer(int sizeHint)
{
Debug.Assert(_rentedBuffer != null);

if (sizeHint < 0)
{
throw new ArgumentException(nameof(sizeHint));
}

if (sizeHint == 0)
{
sizeHint = MinimumBufferSize;
}

var availableSpace = _rentedBuffer.Length - _index;

if (sizeHint > availableSpace)
{
var growBy = Math.Max(sizeHint, _rentedBuffer.Length);

var newSize = checked(_rentedBuffer.Length + growBy);

var oldBuffer = _rentedBuffer;

_rentedBuffer = ArrayPool<T>.Shared.Rent(newSize);

Debug.Assert(oldBuffer.Length >= _index);
Debug.Assert(_rentedBuffer.Length >= _index);

var previousBuffer = oldBuffer.AsSpan(0, _index);
previousBuffer.CopyTo(_rentedBuffer);
previousBuffer.Clear();
ArrayPool<T>.Shared.Return(oldBuffer);
}

Debug.Assert(_rentedBuffer.Length - _index > 0);
Debug.Assert(_rentedBuffer.Length - _index >= sizeHint);
}

private static void ThrowInvalidOperationException(int capacity)
{
throw new InvalidOperationException($"Cannot advance past the end of the buffer, which has a size of {capacity}.");
}
}
}
Loading