BCAT fixes. Tweak how U8StringBuilder is used

This commit is contained in:
Alex Barney 2020-04-11 22:18:43 -07:00
parent 6e4372ce58
commit f3452bb314
15 changed files with 200 additions and 109 deletions

View file

@ -32,7 +32,7 @@ namespace LibHac.Bcat
public Result GetServiceCreator(out IServiceCreator serviceCreator, BcatServiceType type)
{
if ((uint)type < ServiceTypeCount)
if ((uint)type >= ServiceTypeCount)
{
serviceCreator = default;
return ResultLibHac.ArgumentOutOfRange.Log();
@ -87,6 +87,9 @@ namespace LibHac.Bcat
if (!rc.IsSuccess())
throw new HorizonResultException(rc, "Abort");
fsClient.SetAccessLogTarget(AccessLogTarget.All);
FsClient = fsClient;
return fsClient;
}
}

View file

@ -2,7 +2,7 @@
namespace LibHac.Bcat.Detail.Ipc
{
public interface IDeliveryCacheDirectoryService
public interface IDeliveryCacheDirectoryService : IDisposable
{
Result Open(ref DirectoryName name);
Result Read(out int entriesRead, Span<DeliveryCacheDirectoryEntry> entryBuffer);

View file

@ -2,7 +2,7 @@
namespace LibHac.Bcat.Detail.Ipc
{
public interface IDeliveryCacheFileService
public interface IDeliveryCacheFileService : IDisposable
{
Result Open(ref DirectoryName directoryName, ref FileName fileName);
Result Read(out long bytesRead, long offset, Span<byte> destination);

View file

@ -2,7 +2,7 @@
namespace LibHac.Bcat.Detail.Ipc
{
public interface IDeliveryCacheStorageService
public interface IDeliveryCacheStorageService : IDisposable
{
Result CreateFileService(out IDeliveryCacheFileService fileService);
Result CreateDirectoryService(out IDeliveryCacheDirectoryService directoryService);

View file

@ -48,8 +48,8 @@ namespace LibHac.Bcat.Detail.Service.Core
// Get the mount name
var mountName = new MountName();
new U8StringBuilder(mountName.Name)
.Append(DeliveryCacheMountNamePrefix)
var sb = new U8StringBuilder(mountName.Name);
sb.Append(DeliveryCacheMountNamePrefix)
.AppendFormat(index, 'd', 2);
// Mount the save if enabled
@ -89,8 +89,8 @@ namespace LibHac.Bcat.Detail.Service.Core
{
var mountName = new MountName();
new U8StringBuilder(mountName.Name)
.Append(DeliveryCacheMountNamePrefix)
var sb = new U8StringBuilder(mountName.Name);
sb.Append(DeliveryCacheMountNamePrefix)
.AppendFormat(index, 'd', 2);
// Unmount the entry's savedata
@ -115,8 +115,8 @@ namespace LibHac.Bcat.Detail.Service.Core
var mountName = new MountName();
new U8StringBuilder(mountName.Name)
.Append(DeliveryCacheMountNamePrefix)
var sb = new U8StringBuilder(mountName.Name);
sb.Append(DeliveryCacheMountNamePrefix)
.AppendFormat(index, 'd', 2);
if (!DisableStorage)
@ -215,10 +215,10 @@ namespace LibHac.Bcat.Detail.Service.Core
var sb = new U8StringBuilder(pathBuffer);
AppendMountName(ref sb, applicationId);
sb.Append(DirectoriesPath);
sb.Append(DirectorySeparator).Append(directoryName.Bytes);
sb.Append(DirectorySeparator).Append(FilesDirectoryName);
sb.Append(DirectorySeparator).Append(fileName.Bytes);
sb.Append(DirectoriesPath)
.Append(DirectorySeparator).Append(directoryName.Bytes)
.Append(DirectorySeparator).Append(FilesDirectoryName)
.Append(DirectorySeparator).Append(fileName.Bytes);
}
}
@ -229,9 +229,9 @@ namespace LibHac.Bcat.Detail.Service.Core
var sb = new U8StringBuilder(pathBuffer);
AppendMountName(ref sb, applicationId);
sb.Append(DirectoriesPath);
sb.Append(DirectorySeparator).Append(directoryName.Bytes);
sb.Append(DirectorySeparator).Append(FilesMetaFileName);
sb.Append(DirectoriesPath)
.Append(DirectorySeparator).Append(directoryName.Bytes)
.Append(DirectorySeparator).Append(FilesMetaFileName);
}
}
@ -252,8 +252,8 @@ namespace LibHac.Bcat.Detail.Service.Core
var sb = new U8StringBuilder(pathBuffer);
AppendMountName(ref sb, applicationId);
sb.Append(DirectoriesPath);
sb.Append(DirectorySeparator).Append(directoryName.Bytes);
sb.Append(DirectoriesPath)
.Append(DirectorySeparator).Append(directoryName.Bytes);
}
}

View file

@ -4,11 +4,13 @@ using LibHac.Bcat.Detail.Service.Core;
namespace LibHac.Bcat.Detail.Service
{
internal class DeliveryCacheDirectoryService : IDeliveryCacheDirectoryService, IDisposable
internal class DeliveryCacheDirectoryService : IDeliveryCacheDirectoryService
{
private BcatServer Server { get; }
private object Locker { get; } = new object();
private DeliveryCacheStorageService Parent { get; }
// ReSharper disable once UnusedAutoPropertyAccessor.Local
private AccessControl Access { get; }
private ulong ApplicationId { get; }
private DirectoryName _name;

View file

@ -6,11 +6,13 @@ using LibHac.Fs;
namespace LibHac.Bcat.Detail.Service
{
internal class DeliveryCacheFileService : IDeliveryCacheFileService, IDisposable
internal class DeliveryCacheFileService : IDeliveryCacheFileService
{
private BcatServer Server { get; }
private object Locker { get; } = new object();
private DeliveryCacheStorageService Parent { get; }
// ReSharper disable once UnusedAutoPropertyAccessor.Local
private AccessControl Access { get; }
private ulong ApplicationId { get; }
private FileHandle _handle;

View file

@ -4,12 +4,12 @@ using LibHac.Bcat.Detail.Ipc;
namespace LibHac.Bcat.Detail.Service
{
internal class DeliveryCacheStorageService : IDeliveryCacheStorageService, IDisposable
internal class DeliveryCacheStorageService : IDeliveryCacheStorageService
{
private const int MaxOpenCount = 8;
private BcatServer Server { get; }
public object Locker { get; } = new object();
private object Locker { get; } = new object();
private AccessControl Access { get; }
private ulong ApplicationId { get; }
private int FileServiceOpenCount { get; set; }

View file

@ -6,6 +6,8 @@ namespace LibHac.Bcat.Detail.Service
internal class ServiceCreator : IServiceCreator
{
private BcatServer Server { get; }
// ReSharper disable once UnusedAutoPropertyAccessor.Local
private BcatServiceType ServiceType { get; }
private AccessControl AccessControl { get; }
@ -35,16 +37,9 @@ namespace LibHac.Bcat.Detail.Service
Result rc = Server.GetStorageManager().Open(applicationId.Value);
if (rc.IsFailure()) return rc;
try
{
// todo: Check if network account required
// todo: Check if network account required
service = new DeliveryCacheStorageService(Server, applicationId.Value, AccessControl);
}
finally
{
Server.GetStorageManager().Release(applicationId.Value);
}
service = new DeliveryCacheStorageService(Server, applicationId.Value, AccessControl);
return Result.Success;
}

View file

@ -171,7 +171,7 @@ namespace LibHac.Common
public static bool IsAlpha(byte c)
{
return (c | 0x20u) - (byte)'A' <= 'Z' - 'A';
return (c | 0x20u) - (byte)'a' <= 'z' - 'a';
}
public static bool IsDigit(byte c)

View file

@ -10,18 +10,15 @@ namespace LibHac.Common
{
private const int NullTerminatorLength = 1;
private readonly Span<byte> _buffer;
private int _length;
public Span<byte> Buffer { get; }
public int Length { get; private set; }
public bool Overflowed { get; private set; }
public readonly int Length => _length;
public readonly int Capacity => _buffer.Length - NullTerminatorLength;
public readonly Span<byte> Buffer => _buffer;
public readonly int Capacity => Buffer.Length - NullTerminatorLength;
public U8StringBuilder(Span<byte> buffer)
{
_buffer = buffer;
_length = 0;
Buffer = buffer;
Length = 0;
Overflowed = false;
ThrowIfBufferLengthIsZero();
@ -29,70 +26,69 @@ namespace LibHac.Common
AddNullTerminator();
}
public U8StringBuilder Append(ReadOnlySpan<byte> value)
// These functions are internal so they can be called by the extension methods
// in U8StringBuilderExtensions. It's not an ideal setup, but it allows append
// calls to be chained without accidentally creating a copy of the U8StringBuilder.
internal void AppendInternal(ReadOnlySpan<byte> value)
{
if (Overflowed) return this;
if (Overflowed) return;
int valueLength = StringUtils.GetLength(value);
if (!HasAdditionalCapacity(valueLength))
{
Overflowed = true;
return this;
return;
}
value.Slice(0, valueLength).CopyTo(_buffer.Slice(_length));
_length += valueLength;
value.Slice(0, valueLength).CopyTo(Buffer.Slice(Length));
Length += valueLength;
AddNullTerminator();
return this;
}
public U8StringBuilder Append(byte value)
internal void AppendInternal(byte value)
{
if (Overflowed) return this;
if (Overflowed) return;
if (!HasAdditionalCapacity(1))
{
Overflowed = true;
return this;
return;
}
_buffer[_length] = value;
_length++;
Buffer[Length] = value;
Length++;
AddNullTerminator();
return this;
}
public U8StringBuilder AppendFormat(byte value, char format = 'G', byte precision = 255) =>
internal void AppendFormatInternal(byte value, char format = 'G', byte precision = 255) =>
AppendFormatUInt64(value, format, precision);
public U8StringBuilder AppendFormat(sbyte value, char format = 'G', byte precision = 255) =>
internal void AppendFormatInternal(sbyte value, char format = 'G', byte precision = 255) =>
AppendFormatInt64(value, 0xff, format, precision);
public U8StringBuilder AppendFormat(ushort value, char format = 'G', byte precision = 255) =>
internal void AppendFormatInternal(ushort value, char format = 'G', byte precision = 255) =>
AppendFormatUInt64(value, format, precision);
public U8StringBuilder AppendFormat(short value, char format = 'G', byte precision = 255) =>
internal void AppendFormatInternal(short value, char format = 'G', byte precision = 255) =>
AppendFormatInt64(value, 0xffff, format, precision);
public U8StringBuilder AppendFormat(uint value, char format = 'G', byte precision = 255) =>
internal void AppendFormatInternal(uint value, char format = 'G', byte precision = 255) =>
AppendFormatUInt64(value, format, precision);
public U8StringBuilder AppendFormat(int value, char format = 'G', byte precision = 255) =>
internal void AppendFormatInternal(int value, char format = 'G', byte precision = 255) =>
AppendFormatInt64(value, 0xffffff, format, precision);
public U8StringBuilder AppendFormat(ulong value, char format = 'G', byte precision = 255) =>
internal void AppendFormatInternal(ulong value, char format = 'G', byte precision = 255) =>
AppendFormatUInt64(value, format, precision);
public U8StringBuilder AppendFormat(long value, char format = 'G', byte precision = 255) =>
internal void AppendFormatInternal(long value, char format = 'G', byte precision = 255) =>
AppendFormatInt64(value, 0xffffffff, format, precision);
public U8StringBuilder AppendFormat(float value, char format = 'G', byte precision = 255) =>
internal void AppendFormatInternal(float value, char format = 'G', byte precision = 255) =>
AppendFormatFloat(value, format, precision);
public U8StringBuilder AppendFormat(double value, char format = 'G', byte precision = 255) =>
internal void AppendFormatInternal(double value, char format = 'G', byte precision = 255) =>
AppendFormatDouble(value, format, precision);
private readonly bool HasCapacity(int requiredCapacity)
@ -102,22 +98,22 @@ namespace LibHac.Common
private readonly bool HasAdditionalCapacity(int requiredAdditionalCapacity)
{
return HasCapacity(_length + requiredAdditionalCapacity);
return HasCapacity(Length + requiredAdditionalCapacity);
}
private void AddNullTerminator()
{
_buffer[_length] = 0;
Buffer[Length] = 0;
}
private readonly void ThrowIfBufferLengthIsZero()
{
if (_buffer.Length == 0) throw new ArgumentException("Buffer length must be greater than 0.");
if (Buffer.Length == 0) throw new ArgumentException("Buffer length must be greater than 0.");
}
private U8StringBuilder AppendFormatInt64(long value, ulong mask, char format, byte precision)
private void AppendFormatInt64(long value, ulong mask, char format, byte precision)
{
if (Overflowed) return this;
if (Overflowed) return;
// Remove possible sign extension if needed
if (mask == 'x' | mask == 'X')
@ -126,7 +122,7 @@ namespace LibHac.Common
}
// Exclude the null terminator from the buffer because Utf8Formatter doesn't handle it
Span<byte> availableBuffer = _buffer.Slice(_length, Capacity - _length);
Span<byte> availableBuffer = Buffer.Slice(Length, Capacity - Length);
bool bufferLargeEnough = Utf8Formatter.TryFormat(value, availableBuffer, out int bytesWritten,
new StandardFormat(format, precision));
@ -134,21 +130,19 @@ namespace LibHac.Common
if (!bufferLargeEnough)
{
Overflowed = true;
return this;
return;
}
_length += bytesWritten;
Length += bytesWritten;
AddNullTerminator();
return this;
}
private U8StringBuilder AppendFormatUInt64(ulong value, char format, byte precision)
private void AppendFormatUInt64(ulong value, char format, byte precision)
{
if (Overflowed) return this;
if (Overflowed) return;
// Exclude the null terminator from the buffer because Utf8Formatter doesn't handle it
Span<byte> availableBuffer = _buffer.Slice(_length, Capacity - _length);
Span<byte> availableBuffer = Buffer.Slice(Length, Capacity - Length);
bool bufferLargeEnough = Utf8Formatter.TryFormat(value, availableBuffer, out int bytesWritten,
new StandardFormat(format, precision));
@ -156,21 +150,19 @@ namespace LibHac.Common
if (!bufferLargeEnough)
{
Overflowed = true;
return this;
return;
}
_length += bytesWritten;
Length += bytesWritten;
AddNullTerminator();
return this;
}
private U8StringBuilder AppendFormatFloat(float value, char format, byte precision)
private void AppendFormatFloat(float value, char format, byte precision)
{
if (Overflowed) return this;
if (Overflowed) return;
// Exclude the null terminator from the buffer because Utf8Formatter doesn't handle it
Span<byte> availableBuffer = _buffer.Slice(_length, Capacity - _length);
Span<byte> availableBuffer = Buffer.Slice(Length, Capacity - Length);
bool bufferLargeEnough = Utf8Formatter.TryFormat(value, availableBuffer, out int bytesWritten,
new StandardFormat(format, precision));
@ -178,21 +170,19 @@ namespace LibHac.Common
if (!bufferLargeEnough)
{
Overflowed = true;
return this;
return;
}
_length += bytesWritten;
Length += bytesWritten;
AddNullTerminator();
return this;
}
private U8StringBuilder AppendFormatDouble(double value, char format, byte precision)
private void AppendFormatDouble(double value, char format, byte precision)
{
if (Overflowed) return this;
if (Overflowed) return;
// Exclude the null terminator from the buffer because Utf8Formatter doesn't handle it
Span<byte> availableBuffer = _buffer.Slice(_length, Capacity - _length);
Span<byte> availableBuffer = Buffer.Slice(Length, Capacity - Length);
bool bufferLargeEnough = Utf8Formatter.TryFormat(value, availableBuffer, out int bytesWritten,
new StandardFormat(format, precision));
@ -200,15 +190,98 @@ namespace LibHac.Common
if (!bufferLargeEnough)
{
Overflowed = true;
return this;
return;
}
_length += bytesWritten;
Length += bytesWritten;
AddNullTerminator();
return this;
}
public override readonly string ToString() => StringUtils.Utf8ZToString(_buffer);
public override readonly string ToString() => StringUtils.Utf8ZToString(Buffer);
}
public static class U8StringBuilderExtensions
{
public static ref U8StringBuilder Append(this ref U8StringBuilder sb, ReadOnlySpan<byte> value)
{
sb.AppendInternal(value);
return ref sb;
}
public static ref U8StringBuilder Append(this ref U8StringBuilder sb, byte value)
{
sb.AppendInternal(value);
return ref sb;
}
public static ref U8StringBuilder AppendFormat(this ref U8StringBuilder sb, byte value, char format = 'G',
byte precision = 255)
{
sb.AppendFormatInternal(value, format, precision);
return ref sb;
}
public static ref U8StringBuilder AppendFormat(this ref U8StringBuilder sb, sbyte value, char format = 'G',
byte precision = 255)
{
sb.AppendFormatInternal(value, format, precision);
return ref sb;
}
public static ref U8StringBuilder AppendFormat(this ref U8StringBuilder sb, ushort value, char format = 'G',
byte precision = 255)
{
sb.AppendFormatInternal(value, format, precision);
return ref sb;
}
public static ref U8StringBuilder AppendFormat(this ref U8StringBuilder sb, short value, char format = 'G',
byte precision = 255)
{
sb.AppendFormatInternal(value, format, precision);
return ref sb;
}
public static ref U8StringBuilder AppendFormat(this ref U8StringBuilder sb, uint value, char format = 'G',
byte precision = 255)
{
sb.AppendFormatInternal(value, format, precision);
return ref sb;
}
public static ref U8StringBuilder AppendFormat(this ref U8StringBuilder sb, int value, char format = 'G',
byte precision = 255)
{
sb.AppendFormatInternal(value, format, precision);
return ref sb;
}
public static ref U8StringBuilder AppendFormat(this ref U8StringBuilder sb, ulong value, char format = 'G',
byte precision = 255)
{
sb.AppendFormatInternal(value, format, precision);
return ref sb;
}
public static ref U8StringBuilder AppendFormat(this ref U8StringBuilder sb, long value, char format = 'G',
byte precision = 255)
{
sb.AppendFormatInternal(value, format, precision);
return ref sb;
}
public static ref U8StringBuilder AppendFormat(this ref U8StringBuilder sb, float value, char format = 'G',
byte precision = 255)
{
sb.AppendFormatInternal(value, format, precision);
return ref sb;
}
public static ref U8StringBuilder AppendFormat(this ref U8StringBuilder sb, double value, char format = 'G',
byte precision = 255)
{
sb.AppendFormatInternal(value, format, precision);
return ref sb;
}
}
}

View file

@ -28,9 +28,10 @@ namespace LibHac.Fs.Shim
Debug.Assert(nameBuffer.Length >= requiredNameBufferSize);
// ReSharper disable once RedundantAssignment
int size = new U8StringBuilder(nameBuffer).Append(mountName).Append(StringTraits.DriveSeparator).Length;
Debug.Assert(size == requiredNameBufferSize - 1);
var sb = new U8StringBuilder(nameBuffer);
sb.Append(mountName).Append(StringTraits.DriveSeparator);
Debug.Assert(sb.Length == requiredNameBufferSize - 1);
return Result.Success;
}
@ -144,7 +145,8 @@ namespace LibHac.Fs.Shim
? StringTraits.NullTerminator
: StringTraits.DirectorySeparator;
Result rc = new U8StringBuilder(sfPath.Str).Append(rootPath).Append(endingSeparator).ToSfPath();
var sb = new U8StringBuilder(sfPath.Str);
Result rc = sb.Append(rootPath).Append(endingSeparator).ToSfPath();
if (rc.IsFailure()) return rc;
}
else

View file

@ -43,9 +43,10 @@ namespace LibHac.Fs.Shim
if (nameBuffer.Length < requiredNameBufferSize)
return ResultFs.TooLongPath.Log();
// ReSharper disable once RedundantAssignment
int size = new U8StringBuilder(nameBuffer).Append(HostRootFileSystemPath).Append(_path.Str).Length;
Debug.Assert(size == requiredNameBufferSize - 1);
var sb = new U8StringBuilder(nameBuffer);
sb.Append(HostRootFileSystemPath).Append(_path.Str);
Debug.Assert(sb.Length == requiredNameBufferSize - 1);
return Result.Success;
}

View file

@ -26,9 +26,10 @@ namespace LibHac.FsSystem
{
fsPath = new FsPath();
U8StringBuilder builder = new U8StringBuilder(fsPath.Str).Append(path);
var sb = new U8StringBuilder(fsPath.Str);
bool overflowed = sb.Append(path).Overflowed;
return builder.Overflowed ? ResultFs.TooLongPath.Log() : Result.Success;
return overflowed ? ResultFs.TooLongPath.Log() : Result.Success;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

View file

@ -10,14 +10,14 @@ namespace LibHac
public class Horizon
{
internal ITimeSpanGenerator Time { get; }
private FileSystemServer FileSystemServer { get; set; }
private BcatServer BcatServer { get; set; }
public FileSystemServer FileSystemServer { get; private set; }
public BcatServer BcatServer { get; private set; }
private readonly object _initLocker = new object();
public Horizon(ITimeSpanGenerator timer)
{
Time = timer;
Time = timer ?? new StopWatchTimeSpanGenerator();
}
public Result OpenFileSystemProxyService(out IFileSystemProxy service)
@ -60,6 +60,18 @@ namespace LibHac
return BcatServer.GetServiceCreator(out service, type);
}
public void InitializeBcatServer()
{
if (BcatServer != null) return;
lock (_initLocker)
{
if (BcatServer != null) return;
BcatServer = new BcatServer(this);
}
}
public void InitializeFileSystemServer(FileSystemCreators fsCreators, IDeviceOperator deviceOperator)
{
if (FileSystemServer != null) return;