diff --git a/Source/Code/UtilPack.JSON.Configuration/StringContents.cs b/Source/Code/UtilPack.JSON.Configuration/StringContents.cs new file mode 100644 index 0000000..cf69f52 --- /dev/null +++ b/Source/Code/UtilPack.JSON.Configuration/StringContents.cs @@ -0,0 +1,157 @@ +/* + * Copyright 2019 Stanislav Muhametsin. All rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + */ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.FileProviders; +using System; +using System.Text; + +namespace UtilPack.JSON.Configuration +{ + internal sealed class StringContentFileProvider : IFileProvider + { + public const String PATH = ":::non-existing:::"; + + private readonly FileInfo _fileInfo; + + + public StringContentFileProvider( String stringContents ) + : this( new FileInfo( stringContents ) ) + { + + } + + public StringContentFileProvider( Byte[] serializedContents ) + : this( new FileInfo( serializedContents ) ) + { + } + + private StringContentFileProvider( FileInfo info ) + { + this._fileInfo = ArgumentValidator.ValidateNotNull( nameof( info ), info ); + } + + + + public IDirectoryContents GetDirectoryContents( String subpath ) + { + return NotFoundDirectoryContents.Singleton; + } + + public IFileInfo GetFileInfo( String subpath ) + { + return String.Equals( PATH, subpath, StringComparison.Ordinal ) ? + this._fileInfo : + null; + } + + public Microsoft.Extensions.Primitives.IChangeToken Watch( String filter ) + { + return ChangeToken.Instance; + } + + private sealed class FileInfo : IFileInfo + { + + private static readonly Encoding TheEncoding = new UTF8Encoding( false, false ); + + private readonly Byte[] _contentsAsBytes; + + public FileInfo( String stringContents ) + : this( TheEncoding.GetBytes( stringContents ) ) + { + + } + + public FileInfo( Byte[] serializedContents ) + { + this._contentsAsBytes = serializedContents; + } + + public Boolean Exists => true; + + public Int64 Length => this._contentsAsBytes.Length; + + public String PhysicalPath => PATH; + + public String Name => PATH; + + public DateTimeOffset LastModified => DateTimeOffset.MinValue; + + public Boolean IsDirectory => false; + + public System.IO.Stream CreateReadStream() + { + return new System.IO.MemoryStream( this._contentsAsBytes, 0, this._contentsAsBytes.Length, false, false ); + } + } + + private sealed class ChangeToken : Microsoft.Extensions.Primitives.IChangeToken + { + public static ChangeToken Instance = new ChangeToken(); + + private ChangeToken() + { + + } + + public Boolean HasChanged => false; + + public Boolean ActiveChangeCallbacks => true; + + public IDisposable RegisterChangeCallback( + Action callback, + Object state + ) + { + return NoOpDisposable.Instance; + } + } + + + } + + /// + /// This class contains extension methods for types defined in other assemblies. + /// + public static partial class JsonConfigurationExtensions + { + /// + /// Adds the given string as JSON content for this . + /// + /// This . + /// The JSON contents, as . + /// The . + public static IConfigurationBuilder AddJsonContents( this IConfigurationBuilder builder, String textualContents ) + { + return ArgumentValidator.ValidateNotNullReference( builder ) + .AddJsonFile( new StringContentFileProvider( textualContents ), StringContentFileProvider.PATH, false, false ); + } + + /// + /// Adds the given serialized string as JSON content for this . + /// + /// This . + /// The JSON contents, as array. + /// The . + public static IConfigurationBuilder AddJsonContents( this IConfigurationBuilder builder, Byte[] stringAsBytes ) + { + return ArgumentValidator.ValidateNotNullReference( builder ) + .AddJsonFile( new StringContentFileProvider( stringAsBytes ), StringContentFileProvider.PATH, false, false ); + } + } +} diff --git a/Source/Code/UtilPack.JSON.Configuration/UtilPack.JSON.Configuration.csproj b/Source/Code/UtilPack.JSON.Configuration/UtilPack.JSON.Configuration.csproj new file mode 100644 index 0000000..67dbb7d --- /dev/null +++ b/Source/Code/UtilPack.JSON.Configuration/UtilPack.JSON.Configuration.csproj @@ -0,0 +1,50 @@ + + + + netstandard1.3;netstandard2.0;net451 + + + + True + + + + + + + + 1.1.2 + 2.2.0 + + + + + + + + + + + + + + + 1.0.0 + + $(AssemblyName) + Small library containing extension methods for Microsoft.Extensions.Configuiration.Json package, including methods to add JSON contents as string or byte array. + + + + + $(AssemblyName) + $(VersionPrefix) + $(PackageVersion)-$(VersionSuffix) + Initial release. + json utility microsoft configuration extensions json + UtilPack: Extensions for Microsoft.Extensions.Configuration.Json + + + + + \ No newline at end of file diff --git a/Source/Code/UtilPack/BinaryExtensions.cs b/Source/Code/UtilPack/BinaryExtensions.cs index d311dd8..c64d94d 100644 --- a/Source/Code/UtilPack/BinaryExtensions.cs +++ b/Source/Code/UtilPack/BinaryExtensions.cs @@ -20,6 +20,8 @@ using System.IO; using System.Linq; using System.Text; +using System.Threading; +using System.Threading.Tasks; using UtilPack; namespace UtilPack @@ -220,62 +222,63 @@ public static Stream SeekFromBegin( this Stream stream, Int64 position ) // return (Byte) b; // } - // /// - // /// Reads a whole stream and returns its contents as single byte array. - // /// - // /// The stream to read. - // /// The optional buffer to use. If not specified, then a buffer of 1024 bytes will be used. The buffer will only be used if stream does not support querying length and position. - // /// The stream contents as single byte array. - // /// If is null. - //#if !NET40 - // [System.Runtime.CompilerServices.MethodImpl( System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining )] - //#endif - // public static Byte[] ReadUntilTheEnd( this Stream stream, Byte[] buffer = null ) - // { - // Int64 arrayLen = -1; - // if ( stream.CanSeek ) - // { - // try - // { - // arrayLen = stream.Length - stream.Position; - // } - // catch ( NotSupportedException ) - // { - // // stream can't be queried for length or position - // } - // } + /// + /// Reads a whole stream and returns its contents as single byte array. + /// + /// The stream to read. + /// The optional buffer to use. If not specified, then a buffer of 1024 bytes will be used. The buffer will only be used if stream does not support querying length and position.< + /// The . + /// The stream contents as single byte array. + /// If is null. +#if !NET40 + [System.Runtime.CompilerServices.MethodImpl( System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining )] +#endif + public static async Task ReadUntilTheEndAsync( this Stream stream, CancellationToken token, Byte[] buffer = null ) + { + Int64 arrayLen = -1; + if ( stream.CanSeek ) + { + try + { + arrayLen = stream.Length - stream.Position; + } + catch ( NotSupportedException ) + { + // stream can't be queried for length or position + } + } - // Byte[] retVal; - // if ( arrayLen < 0 ) - // { - // // Have to read using the buffer. - // if ( buffer == null ) - // { - // buffer = new Byte[1024]; - // } + Byte[] retVal; + if ( arrayLen < 0 ) + { + // Have to read using the buffer. + if ( buffer == null ) + { + buffer = new Byte[1024]; + } - // using ( var ms = new MemoryStream() ) - // { - // Int32 read; - // while ( ( read = stream.Read( buffer, 0, buffer.Length ) ) > 0 ) - // { - // ms.Write( buffer, 0, read ); - // } - // retVal = ms.ToArray(); - // } - // } - // else if ( arrayLen == 0 ) - // { - // retVal = Empty.Array; - // } - // else - // { - // retVal = new Byte[arrayLen]; - // stream.ReadWholeArray( retVal ); - // } + using ( var ms = new MemoryStream() ) + { + Int32 read; + while ( ( read = await stream.ReadAsync( buffer, 0, buffer.Length, token ) ) > 0 ) + { + ms.Write( buffer, 0, read ); + } + retVal = ms.ToArray(); + } + } + else if ( arrayLen == 0 ) + { + retVal = Empty.Array; + } + else + { + retVal = new Byte[arrayLen]; + await stream.ReadAtLeastAsync( retVal, 0, retVal.Length, retVal.Length, token ); + } - // return retVal; - // } + return retVal; + } /// /// Reads a single byte at specified index in byte array. diff --git a/Source/Code/UtilPack/StreamRelated.cs b/Source/Code/UtilPack/StreamRelated.cs index 1c59f0c..15708dd 100644 --- a/Source/Code/UtilPack/StreamRelated.cs +++ b/Source/Code/UtilPack/StreamRelated.cs @@ -1626,6 +1626,7 @@ public static Int32 AppendToBytes( this StreamWriterWithResizableBuffer writer, } + // TODO remove this method in 2.0, as there is overload with cancellation token which should be used. /// /// Using given chunk read size, read at least the given amount of bytes into the given array from this stream. /// @@ -1649,6 +1650,32 @@ public static async Task ReadAtLeastAsync( this Stream stream, Byte[] arr offset += readCount; } return offset - originalOffset; + } + + /// + /// Using given chunk read size, read at least the given amount of bytes into the given array from this stream. + /// + /// This stream. + /// The byte array to write data to. + /// The offset in where to start writing. + /// The minimum amount to read. + /// The maximum amount of bytes to read at a time. + /// The to use. + /// Asynchronously returns amount of bytes read. + public static async Task ReadAtLeastAsync( this Stream stream, Byte[] array, Int32 offset, Int32 minAmount, Int32 chunkCount, CancellationToken token ) + { + var originalOffset = offset; + while ( minAmount > 0 ) + { + var readCount = await stream.ReadAsync( array, offset, chunkCount, token ); + if ( readCount <= 0 ) + { + throw new EndOfStreamException(); + } + minAmount -= readCount; + offset += readCount; + } + return offset - originalOffset; } /// diff --git a/Source/Code/UtilPack/UtilPack.csproj b/Source/Code/UtilPack/UtilPack.csproj index aa34ef5..82543ff 100644 --- a/Source/Code/UtilPack/UtilPack.csproj +++ b/Source/Code/UtilPack/UtilPack.csproj @@ -24,7 +24,7 @@ - 1.7.1 + 1.7.2 UtilPack.Common Library containing useful and generic methods, which are missing from one or more BCL. @@ -35,7 +35,7 @@ $(AssemblyName) $(VersionPrefix) $(PackageVersion)-$(VersionSuffix) - Removed Prepend and Append extension methods for IEnumerable<T> from netstandard2.0 version. + Restored method to read the remaining bytes in stream into single byte array. bcl utility binary serialization collection extensions Utility Package for CLR diff --git a/Source/UtilPack.sln b/Source/UtilPack.sln index 1f1ebbb..26d5d74 100644 --- a/Source/UtilPack.sln +++ b/Source/UtilPack.sln @@ -19,7 +19,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UtilPack.Documentation", "C EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.UtilPack", "Tests\Tests.UtilPack\Tests.UtilPack.csproj", "{9F1E8C76-8B7E-43B1-821B-6D7854680B09}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.UtilPack.TabularData", "Tests\Tests.UtilPack.TabularData\Tests.UtilPack.TabularData.csproj", "{59394102-BC30-439D-9710-4C3E04E4CCF3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.UtilPack.TabularData", "Tests\Tests.UtilPack.TabularData\Tests.UtilPack.TabularData.csproj", "{59394102-BC30-439D-9710-4C3E04E4CCF3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UtilPack.JSON.Configuration", "Code\UtilPack.JSON.Configuration\UtilPack.JSON.Configuration.csproj", "{8B2971AA-7712-4591-ADA1-3949DEBC0D90}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -63,6 +65,10 @@ Global {59394102-BC30-439D-9710-4C3E04E4CCF3}.Debug|Any CPU.Build.0 = Debug|Any CPU {59394102-BC30-439D-9710-4C3E04E4CCF3}.Release|Any CPU.ActiveCfg = Release|Any CPU {59394102-BC30-439D-9710-4C3E04E4CCF3}.Release|Any CPU.Build.0 = Release|Any CPU + {8B2971AA-7712-4591-ADA1-3949DEBC0D90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B2971AA-7712-4591-ADA1-3949DEBC0D90}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B2971AA-7712-4591-ADA1-3949DEBC0D90}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B2971AA-7712-4591-ADA1-3949DEBC0D90}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE