1
0

Solution restructured to use multiple test projects

This commit is contained in:
2024-07-04 18:22:26 +02:00
parent 508379d704
commit df6763b99b
144 changed files with 387 additions and 1693 deletions

1
.gitignore vendored
View File

@@ -13,6 +13,7 @@
build
coverage.json
coverage.cobertura.xml
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

View File

@@ -17,22 +17,23 @@ build-debug:
tags:
- docker
- lnx
- 64bit
rules:
- if: $CI_COMMIT_TAG == null
script:
- dotnet restore --no-cache --force
- dotnet build -c Debug --nologo --no-restore --no-incremental
- mkdir ./artifacts
- mv ./AMWD.Common/bin/Debug/*.nupkg ./artifacts/
- mv ./AMWD.Common/bin/Debug/*.snupkg ./artifacts/
- mv ./AMWD.Common.AspNetCore/bin/Debug/*.nupkg ./artifacts/
- mv ./AMWD.Common.AspNetCore/bin/Debug/*.snupkg ./artifacts/
- mv ./AMWD.Common.EntityFrameworkCore/bin/Debug/*.nupkg ./artifacts/
- mv ./AMWD.Common.EntityFrameworkCore/bin/Debug/*.snupkg ./artifacts/
- mv ./AMWD.Common.MessagePack/bin/Debug/*.nupkg ./artifacts/
- mv ./AMWD.Common.MessagePack/bin/Debug/*.snupkg ./artifacts/
- mv ./AMWD.Common.Test/bin/Debug/*.nupkg ./artifacts/
- mv ./AMWD.Common.Test/bin/Debug/*.snupkg ./artifacts/
- mv ./src/AMWD.Common/bin/Debug/*.nupkg ./artifacts/
- mv ./src/AMWD.Common/bin/Debug/*.snupkg ./artifacts/
- mv ./src/AMWD.Common.AspNetCore/bin/Debug/*.nupkg ./artifacts/
- mv ./src/AMWD.Common.AspNetCore/bin/Debug/*.snupkg ./artifacts/
- mv ./src/AMWD.Common.EntityFrameworkCore/bin/Debug/*.nupkg ./artifacts/
- mv ./src/AMWD.Common.EntityFrameworkCore/bin/Debug/*.snupkg ./artifacts/
- mv ./src/AMWD.Common.MessagePack/bin/Debug/*.nupkg ./artifacts/
- mv ./src/AMWD.Common.MessagePack/bin/Debug/*.snupkg ./artifacts/
- mv ./src/AMWD.Common.Test/bin/Debug/*.nupkg ./artifacts/
- mv ./src/AMWD.Common.Test/bin/Debug/*.snupkg ./artifacts/
artifacts:
paths:
- artifacts/*.nupkg
@@ -46,15 +47,24 @@ test-debug:
tags:
- docker
- lnx
- 64bit
rules:
- if: $CI_COMMIT_TAG == null
# line-coverage
#coverage: '/Total[^|]*\|\s*([0-9.%]+)/'
# branch-coverage
coverage: '/Total[^|]*\|[^|]*\|\s*([0-9.%]+)/'
#coverage: /Line coverage[\s\S].+%/
coverage: /Branch coverage[\s\S].+%/
before_script:
- dotnet tool install dotnet-reportgenerator-globaltool --tool-path /dotnet-tools
script:
- dotnet restore --no-cache --force
- dotnet test -c Debug --nologo --no-restore
- dotnet test -c Debug --nologo --no-restore /p:CoverletOutputFormat=Cobertura
- /dotnet-tools/reportgenerator "-reports:${CI_PROJECT_DIR}/**/coverage.cobertura.xml" "-targetdir:/reports" -reportType:TextSummary
- cat /reports/Summary.txt
artifacts:
when: always
reports:
coverage_report:
coverage_format: cobertura
path: ./**/coverage.cobertura.xml
@@ -63,22 +73,23 @@ build-release:
tags:
- docker
- lnx
- 64bit
rules:
- if: $CI_COMMIT_TAG != null
script:
- dotnet restore --no-cache --force
- dotnet build -c Release --nologo --no-restore --no-incremental
- mkdir ./artifacts
- mv ./AMWD.Common/bin/Release/*.nupkg ./artifacts/
- mv ./AMWD.Common/bin/Release/*.snupkg ./artifacts/
- mv ./AMWD.Common.AspNetCore/bin/Release/*.nupkg ./artifacts/
- mv ./AMWD.Common.AspNetCore/bin/Release/*.snupkg ./artifacts/
- mv ./AMWD.Common.EntityFrameworkCore/bin/Release/*.nupkg ./artifacts/
- mv ./AMWD.Common.EntityFrameworkCore/bin/Release/*.snupkg ./artifacts/
- mv ./AMWD.Common.MessagePack/bin/Release/*.nupkg ./artifacts/
- mv ./AMWD.Common.MessagePack/bin/Release/*.snupkg ./artifacts/
- mv ./AMWD.Common.Test/bin/Release/*.nupkg ./artifacts/
- mv ./AMWD.Common.Test/bin/Release/*.snupkg ./artifacts/
- mv ./src/AMWD.Common/bin/Release/*.nupkg ./artifacts/
- mv ./src/AMWD.Common/bin/Release/*.snupkg ./artifacts/
- mv ./src/AMWD.Common.AspNetCore/bin/Release/*.nupkg ./artifacts/
- mv ./src/AMWD.Common.AspNetCore/bin/Release/*.snupkg ./artifacts/
- mv ./src/AMWD.Common.EntityFrameworkCore/bin/Release/*.nupkg ./artifacts/
- mv ./src/AMWD.Common.EntityFrameworkCore/bin/Release/*.snupkg ./artifacts/
- mv ./src/AMWD.Common.MessagePack/bin/Release/*.nupkg ./artifacts/
- mv ./src/AMWD.Common.MessagePack/bin/Release/*.snupkg ./artifacts/
- mv ./src/AMWD.Common.Test/bin/Release/*.nupkg ./artifacts/
- mv ./src/AMWD.Common.Test/bin/Release/*.snupkg ./artifacts/
artifacts:
paths:
- artifacts/*.nupkg
@@ -92,15 +103,24 @@ test-release:
tags:
- docker
- lnx
- 64bit
rules:
- if: $CI_COMMIT_TAG != null
# line-coverage
#coverage: '/Total[^|]*\|\s*([0-9.%]+)/'
# branch-coverage
coverage: '/Total[^|]*\|[^|]*\|\s*([0-9.%]+)/'
#coverage: /Line coverage[\s\S].+%/
coverage: /Branch coverage[\s\S].+%/
before_script:
- dotnet tool install dotnet-reportgenerator-globaltool --tool-path /dotnet-tools
script:
- dotnet restore --no-cache --force
- dotnet test -c Release --nologo --no-restore
- dotnet test -c Release --nologo --no-restore /p:CoverletOutputFormat=Cobertura
- /dotnet-tools/reportgenerator "-reports:${CI_PROJECT_DIR}/**/coverage.cobertura.xml" "-targetdir:/reports" -reportType:TextSummary
- cat /reports/Summary.txt
artifacts:
when: always
reports:
coverage_report:
coverage_format: cobertura
path: ./**/coverage.cobertura.xml
deploy-common:
stage: deploy
@@ -110,6 +130,7 @@ deploy-common:
tags:
- docker
- lnx
- 64bit
rules:
- if: $CI_COMMIT_TAG =~ /^v[0-9.]+/
script:
@@ -123,12 +144,13 @@ deploy-aspnet:
tags:
- docker
- lnx
- 64bit
rules:
- if: $CI_COMMIT_TAG =~ /^asp\/v[0-9.]+/
script:
- dotnet nuget push -k $BAGET_APIKEY -s https://nuget.am-wd.de/v3/index.json --skip-duplicate artifacts/AMWD.Common.AspNetCore.*.nupkg
deploy-entityframework:
deploy-efcore:
stage: deploy
dependencies:
- build-release
@@ -136,12 +158,13 @@ deploy-entityframework:
tags:
- docker
- lnx
- 64bit
rules:
- if: $CI_COMMIT_TAG =~ /^efc\/v[0-9.]+/
script:
- dotnet nuget push -k $BAGET_APIKEY -s https://nuget.am-wd.de/v3/index.json --skip-duplicate artifacts/AMWD.Common.EntityFrameworkCore.*.nupkg
deploy-messagepack:
deploy-msgpack:
stage: deploy
dependencies:
- build-release
@@ -149,6 +172,7 @@ deploy-messagepack:
tags:
- docker
- lnx
- 64bit
rules:
- if: $CI_COMMIT_TAG =~ /^msgpack\/v[0-9.]+/
script:
@@ -162,6 +186,7 @@ deploy-test:
tags:
- docker
- lnx
- 64bit
rules:
- if: $CI_COMMIT_TAG =~ /^test\/v[0-9.]+/
script:

View File

@@ -1,3 +0,0 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("UnitTests")]

View File

@@ -1,26 +0,0 @@
namespace AMWD.Common.Packing.Tar.Interfaces
{
/// <summary>
/// Interface of a archive writer.
/// </summary>
public interface IArchiveDataWriter
{
/// <summary>
/// Write <paramref name="count"/> bytes of data from <paramref name="buffer"/> to corresponding archive.
/// </summary>
/// <param name="buffer">The data storage.</param>
/// <param name="count">How many bytes to be written to the corresponding archive.</param>
int Write(byte[] buffer, int count);
/// <summary>
/// Gets a value indicating whether the writer can write.
/// </summary>
bool CanWrite { get; }
}
/// <summary>
/// The writer delegate.
/// </summary>
/// <param name="writer">The writer.</param>
public delegate void WriteDataDelegate(IArchiveDataWriter writer);
}

View File

@@ -1,125 +0,0 @@
using System;
using AMWD.Common.Packing.Tar.Utils;
namespace AMWD.Common.Packing.Tar.Interfaces
{
/// <summary>
/// See "struct star_header" in <a href="https://www.gnu.org/software/tar/manual/html_node/Standard.html" />
/// </summary>
public interface ITarHeader
{
/// <summary>
/// The name field is the file name of the file, with directory names (if any) preceding the file name,
/// separated by slashes.
/// </summary>
/// <remarks>
/// <c>name</c>
/// <br/>
/// Byte offset: <c>0</c>
/// </remarks>
string FileName { get; set; }
/// <summary>
/// The mode field provides nine bits specifying file permissions and three bits to specify
/// the Set UID, Set GID, and Save Text (sticky) modes.
/// When special permissions are required to create a file with a given mode,
/// and the user restoring files from the archive does not hold such permissions,
/// the mode bit(s) specifying those special permissions are ignored.
/// Modes which are not supported by the operating system restoring files from the archive will be ignored.
/// Unsupported modes should be faked up when creating or updating an archive; e.g.,
/// the group permission could be copied from the other permission.
/// </summary>
/// <remarks>
/// <c>mode</c>
/// <br/>
/// Byte offset: <c>100</c>
/// </remarks>
int Mode { get; set; }
/// <summary>
/// The uid field is the numeric user ID of the file owners.
/// If the operating system does not support numeric user ID, this field should be ignored.
/// </summary>
/// <remarks>
/// <c>uid</c>
/// <br/>
/// Byte offset: <c>108</c>
/// </remarks>
int UserId { get; set; }
/// <summary>
/// The gid fields is the numeric group ID of the file owners.
/// If the operating system does not support numeric group ID, this field should be ignored.
/// </summary>
/// <remarks>
/// <c>gid</c>
/// <br/>
/// Byte offset: <c>116</c>
/// </remarks>
int GroupId { get; set; }
/// <summary>
/// The size field is the size of the file in bytes;
/// linked files are archived with this field specified as zero.
/// </summary>
/// <remarks>
/// <c>size</c>
/// <br/>
/// Byte offset: <c>124</c>
/// </remarks>
long SizeInBytes { get; set; }
/// <summary>
/// <para>mtime</para>
/// <para>byte offset: 136</para>
/// The mtime field represents the data modification time of the file at the time it was archived.
/// It represents the integer number of seconds since January 1, 1970, 00:00 Coordinated Universal Time.
/// </summary>
/// <remarks>
/// <c>mtime</c>
/// <br/>
/// Byte offset: <c>136</c>
/// </remarks>
DateTime LastModification { get; set; }
/// <summary>
/// The typeflag field specifies the type of file archived.
/// If a particular implementation does not recognize or permit the specified type,
/// the file will be extracted as if it were a regular file.
/// As this action occurs, tar issues a warning to the standard error.
/// </summary>
/// <remarks>
/// <c>typeflag</c>
/// <br/>
/// Byte offset: <c>156</c>
/// </remarks>
EntryType EntryType { get; set; }
/// <summary>
/// The uname field will contain the ASCII representation of the owner of the file.
/// If found, the user ID is used rather than the value in the uid field.
/// </summary>
/// <remarks>
/// <c>uname</c>
/// <br/>
/// Byte offset: <c>265</c>
/// </remarks>
string UserName { get; set; }
/// <summary>
/// The gname field will contain the ASCII representation of the group of the file.
/// If found, the group ID is used rather than the values in the gid field.
/// </summary>
/// <remarks>
/// <c>gname</c>
/// <br/>
/// Byte offset: <c>297</c>
/// </remarks>
string GroupName { get; set; }
/// <summary>
/// The size of this header.
/// </summary>
int HeaderSize { get; }
}
}

View File

@@ -1,205 +0,0 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using AMWD.Common.Packing.Tar.Interfaces;
using AMWD.Common.Packing.Tar.Utils;
namespace AMWD.Common.Packing.Tar
{
/// <summary>
/// Extract contents of a tar file represented by a stream for the TarReader constructor.
/// </summary>
/// <remarks>
/// Constructs TarReader object to read data from `tarredData` stream.
/// <br />
/// Copied from: <see href="https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Tar/TarReader.cs">DotnetMakeDeb</see>
/// </remarks>
/// <param name="tarredData">A stream to read tar archive from</param>
public class TarReader(Stream tarredData)
{
private readonly byte[] _dataBuffer = new byte[512];
private readonly UsTarHeader _header = new();
private readonly Stream _inStream = tarredData;
private long _remainingBytesInFile;
/// <summary>
/// Gets the file info (the header).
/// </summary>
public ITarHeader FileInfo => _header;
/// <summary>
/// Read all files from an archive to a directory. It creates some child directories to
/// reproduce a file structure from the archive.
/// </summary>
/// <param name="destDirectory">The out directory.</param>
///
/// CAUTION! This method is not safe. It's not tar-bomb proof.
/// {see http://en.wikipedia.org/wiki/Tar_(file_format) }
/// If you are not sure about the source of an archive you extracting,
/// then use MoveNext and Read and handle paths like ".." and "../.." according
/// to your business logic.
public void ReadToEnd(string destDirectory)
{
while (MoveNext(skipData: false))
{
string fileNameFromArchive = FileInfo.FileName;
string totalPath = destDirectory + Path.DirectorySeparatorChar + fileNameFromArchive;
if (UsTarHeader.IsPathSeparator(fileNameFromArchive[fileNameFromArchive.Length - 1]) || FileInfo.EntryType == EntryType.Directory)
{
// Record is a directory
Directory.CreateDirectory(totalPath);
continue;
}
// If record is a file
string fileName = Path.GetFileName(totalPath);
string directory = totalPath.Remove(totalPath.Length - fileName.Length);
Directory.CreateDirectory(directory);
using FileStream file = File.Create(totalPath);
Read(file);
}
}
/// <summary>
/// Read data from the current archive to a Stream.
/// </summary>
/// <param name="dataDestanation">A stream to read data to</param>
/// <seealso cref="MoveNext"/>
public void Read(Stream dataDestanation)
{
Debug.WriteLine("tar stream position Read in: " + _inStream.Position);
int readBytes;
while ((readBytes = Read(out byte[] read)) != -1)
{
Debug.WriteLine("tar stream position Read while(...) : " + _inStream.Position);
dataDestanation.Write(read, 0, readBytes);
}
Debug.WriteLine("tar stream position Read out: " + _inStream.Position);
}
/// <summary>
/// Reads data from the current archive to a buffer array.
/// </summary>
/// <param name="buffer">The buffer array.</param>
/// <returns>The nuber of bytes read.</returns>
protected int Read(out byte[] buffer)
{
if (_remainingBytesInFile == 0)
{
buffer = null;
return -1;
}
int align512 = -1;
long toRead = _remainingBytesInFile - 512;
if (toRead > 0)
{
toRead = 512;
}
else
{
align512 = 512 - (int)_remainingBytesInFile;
toRead = _remainingBytesInFile;
}
int bytesRead = _inStream.Read(_dataBuffer, 0, (int)toRead);
_remainingBytesInFile -= bytesRead;
if (_inStream.CanSeek && align512 > 0)
{
_inStream.Seek(align512, SeekOrigin.Current);
}
else
while (align512 > 0)
{
_inStream.ReadByte();
--align512;
}
buffer = _dataBuffer;
return bytesRead;
}
/// <summary>
/// Check if all bytes in buffer are zeroes
/// </summary>
/// <param name="buffer">buffer to check</param>
/// <returns>true if all bytes are zeroes, otherwise false</returns>
private static bool IsEmpty(IEnumerable<byte> buffer)
{
foreach (byte b in buffer)
{
if (b != 0)
return false;
}
return true;
}
/// <summary>
/// Move internal pointer to a next file in archive.
/// </summary>
/// <param name="skipData">Should be true if you want to read a header only, otherwise false</param>
/// <returns>false on End Of File otherwise true</returns>
///
/// Example:
/// while(MoveNext())
/// {
/// Read(dataDestStream);
/// }
/// <seealso cref="Read(Stream)"/>
public bool MoveNext(bool skipData)
{
Debug.WriteLine("tar stream position MoveNext in: " + _inStream.Position);
if (_remainingBytesInFile > 0)
{
if (!skipData)
{
throw new TarException(
"You are trying to change file while not all the data from the previous one was read. If you do want to skip files use skipData parameter set to true.");
}
// Skip to the end of file.
if (_inStream.CanSeek)
{
long remainer = _remainingBytesInFile % 512;
_inStream.Seek(_remainingBytesInFile + (512 - (remainer == 0 ? 512 : remainer)), SeekOrigin.Current);
}
else
{
while (Read(out _) != -1) ;
}
}
byte[] bytes = _header.GetBytes();
int headerRead = _inStream.Read(bytes, 0, _header.HeaderSize);
if (headerRead < 512)
throw new TarException("Can not read header");
if (IsEmpty(bytes))
{
headerRead = _inStream.Read(bytes, 0, _header.HeaderSize);
if (headerRead == 512 && IsEmpty(bytes))
{
Debug.WriteLine("tar stream position MoveNext out(false): " + _inStream.Position);
return false;
}
throw new TarException("Broken archive");
}
if (_header.UpdateHeaderFromBytes())
{
throw new TarException("Checksum check failed");
}
_remainingBytesInFile = _header.SizeInBytes;
Debug.WriteLine("tar stream position MoveNext out(true): " + _inStream.Position);
return true;
}
}
}

View File

@@ -1,113 +0,0 @@
using System;
using System.IO;
using AMWD.Common.Packing.Tar.Interfaces;
using AMWD.Common.Packing.Tar.Utils;
namespace AMWD.Common.Packing.Tar
{
/// <summary>
/// Writes a tar (see GNU tar) archive to a stream.
/// </summary>
/// <remarks>
/// Copied from: <see href="https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Tar/TarWriter.cs">DotnetMakeDeb</see>
/// </remarks>
public class TarWriter : LegacyTarWriter
{
/// <summary>
/// Initilizes a new instance of the <see cref="TarWriter"/> class.
/// </summary>
/// <param name="outStream">The stream to write the archive to.</param>
public TarWriter(Stream outStream)
: base(outStream)
{ }
/// <summary>
/// Writes an entry header (file, dir, ...) to the archive.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="lastModificationTime">The last modification time.</param>
/// <param name="count">The number of bytes.</param>
/// <param name="userId">The user id.</param>
/// <param name="groupId">The group id.</param>
/// <param name="mode">The access mode.</param>
/// <param name="entryType">The entry type.</param>
protected override void WriteHeader(string name, DateTime lastModificationTime, long count, int userId, int groupId, int mode, EntryType entryType)
{
var tarHeader = new UsTarHeader()
{
FileName = name,
Mode = mode,
UserId = userId,
GroupId = groupId,
SizeInBytes = count,
LastModification = lastModificationTime,
EntryType = entryType,
UserName = Convert.ToString(userId, 8),
GroupName = Convert.ToString(groupId, 8)
};
OutStream.Write(tarHeader.GetHeaderValue(), 0, tarHeader.HeaderSize);
}
/// <summary>
/// Writes an entry header (file, dir, ...) to the archive.
/// Hashes the username and groupname down to a HashCode.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="lastModificationTime">The last modification time.</param>
/// <param name="count">The number of bytes.</param>
/// <param name="userName">The username.</param>
/// <param name="groupName">The group name.</param>
/// <param name="mode">The access mode.</param>
/// <param name="entryType">The entry type.</param>
protected virtual void WriteHeader(string name, DateTime lastModificationTime, long count, string userName, string groupName, int mode, EntryType entryType)
{
WriteHeader(
name: name,
lastModificationTime: lastModificationTime,
count: count,
userId: userName.GetHashCode(),
groupId: groupName.GetHashCode(),
mode: mode,
entryType: entryType);
}
/// <summary>
/// Writes a file to the archive.
/// </summary>
/// <param name="name">The file name.</param>
/// <param name="dataSizeInBytes">The filesize in bytes.</param>
/// <param name="userName">The username.</param>
/// <param name="groupName">The group name.</param>
/// <param name="mode">The access mode.</param>
/// <param name="lastModificationTime">The last modification time.</param>
/// <param name="writeDelegate">The write handle.</param>
public virtual void Write(string name, long dataSizeInBytes, string userName, string groupName, int mode, DateTime lastModificationTime, WriteDataDelegate writeDelegate)
{
var writer = new DataWriter(OutStream, dataSizeInBytes);
WriteHeader(name, lastModificationTime, dataSizeInBytes, userName, groupName, mode, EntryType.File);
while (writer.CanWrite)
{
writeDelegate(writer);
}
AlignTo512(dataSizeInBytes, false);
}
/// <summary>
/// Writes a file to the archive.
/// </summary>
/// <param name="data">The file stream to add to the archive.</param>
/// <param name="dataSizeInBytes">The filesize in bytes.</param>
/// <param name="fileName">The file name.</param>
/// <param name="userId">The user id.</param>
/// <param name="groupId">The group id.</param>
/// <param name="mode">The access mode.</param>
/// <param name="lastModificationTime">The last modification time.</param>
public void Write(Stream data, long dataSizeInBytes, string fileName, string userId, string groupId, int mode,
DateTime lastModificationTime)
{
WriteHeader(fileName, lastModificationTime, dataSizeInBytes, userId, groupId, mode, EntryType.File);
WriteContent(dataSizeInBytes, data);
AlignTo512(dataSizeInBytes, false);
}
}
}

View File

@@ -1,45 +0,0 @@
using System.IO;
using AMWD.Common.Packing.Tar.Interfaces;
namespace AMWD.Common.Packing.Tar.Utils
{
internal class DataWriter : IArchiveDataWriter
{
private readonly long _size;
private long _remainingBytes;
private readonly Stream _stream;
public DataWriter(Stream data, long dataSizeInBytes)
{
_size = dataSizeInBytes;
_remainingBytes = _size;
_stream = data;
}
public bool CanWrite { get; private set; } = true;
public int Write(byte[] buffer, int count)
{
if (_remainingBytes == 0)
{
CanWrite = false;
return -1;
}
int bytesToWrite;
if (_remainingBytes - count < 0)
{
bytesToWrite = (int)_remainingBytes;
}
else
{
bytesToWrite = count;
}
_stream.Write(buffer, 0, bytesToWrite);
_remainingBytes -= bytesToWrite;
return bytesToWrite;
}
}
}

View File

@@ -1,73 +0,0 @@
namespace AMWD.Common.Packing.Tar.Utils
{
/// <summary>
/// See "Values used in typeflag field." in <a href="https://www.gnu.org/software/tar/manual/html_node/Standard.html" />
/// </summary>
public enum EntryType : byte
{
/// <summary>
/// AREGTYPE, regular file
/// </summary>
File = 0,
/// <summary>
/// REGTYPE, regular file
/// </summary>
FileObsolete = 0x30,
/// <summary>
/// LNKTYPE, link
/// </summary>
HardLink = 0x31,
/// <summary>
/// SYMTYPE, reserved
/// </summary>
SymLink = 0x32,
/// <summary>
/// CHRTYPE, character special
/// </summary>
CharDevice = 0x33,
/// <summary>
/// BLKTYPE, block special
/// </summary>
BlockDevice = 0x34,
/// <summary>
/// DIRTYPE, directory
/// </summary>
Directory = 0x35,
/// <summary>
/// FIFOTYPE, FIFO special
/// </summary>
Fifo = 0x36,
/// <summary>
/// CONTTYPE, reserved
/// </summary>
Content = 0x37,
/// <summary>
/// XHDTYPE, Extended header referring to the next file in the archive
/// </summary>
ExtendedHeader = 0x78,
/// <summary>
/// XGLTYPE, Global extended header
/// </summary>
GlobalExtendedHeader = 0x67,
/// <summary>
/// GNUTYPE_LONGLINK, Identifies the *next* file on the tape as having a long linkname.
/// </summary>
LongLink = 0x4b,
/// <summary>
/// GNUTYPE_LONGNAME, Identifies the *next* file on the tape as having a long name.
/// </summary>
LongName = 0x4c
}
}

View File

@@ -1,329 +0,0 @@
using System;
using System.IO;
using System.Text;
using System.Threading;
using AMWD.Common.Packing.Tar.Interfaces;
namespace AMWD.Common.Packing.Tar.Utils
{
/// <summary>
/// Implements a legacy TAR writer.
/// </summary>
/// <remarks>
/// Writes tar (see GNU tar) archive to a stream
/// <br/>
/// Copied from: <see href="https://github.com/ygoe/DotnetMakeDeb/blob/v1.1.0/DotnetMakeDeb/Tar/LegacyTarWriter.cs">DotnetMakeDeb</see>
/// </remarks>
/// <param name="outStream">stream to write archive to</param>
public class LegacyTarWriter(Stream outStream) : IDisposable
{
private readonly Stream _outStream = outStream;
private bool _isClosed;
/// <summary>
/// The buffer for writing.
/// </summary>
protected byte[] buffer = new byte[1024];
/// <summary>
/// Gets or sets a value indicating whether to read on zero.
/// </summary>
public bool ReadOnZero { get; set; } = true;
/// <summary>
/// Gets the output stream.
/// </summary>
protected virtual Stream OutStream
{
get { return _outStream; }
}
#region IDisposable Members
/// <inheritdoc/>
public void Dispose()
=> Close();
#endregion IDisposable Members
/// <summary>
/// Writes a directory entry.
/// </summary>
/// <param name="path">The path to the directory.</param>
/// <param name="userId">The user id.</param>
/// <param name="groupId">The group id.</param>
/// <param name="mode">The access mode.</param>
/// <exception cref="ArgumentNullException"><paramref name="path"/> is not set.</exception>
public void WriteDirectoryEntry(string path, int userId, int groupId, int mode)
{
if (string.IsNullOrEmpty(path))
throw new ArgumentNullException(nameof(path), "The path is not set.");
if (path[path.Length - 1] != '/')
path += '/';
DateTime lastWriteTime;
if (Directory.Exists(path))
{
lastWriteTime = Directory.GetLastWriteTime(path);
}
else
{
lastWriteTime = DateTime.Now;
}
// handle long path names (> 99 characters)
if (path.Length > 99)
{
WriteLongName(
name: path,
userId: userId,
groupId: groupId,
mode: mode,
lastModificationTime: lastWriteTime);
// shorten the path name so it can be written properly
path = path.Substring(0, 99);
}
WriteHeader(path, lastWriteTime, 0, userId, groupId, mode, EntryType.Directory);
}
/// <summary>
/// Writes a directory and its contents.
/// </summary>
/// <param name="directory">The directory.</param>
/// <param name="doRecursive">Write also sub-directories.</param>
/// <exception cref="ArgumentNullException"><paramref name="directory"/> is not set.</exception>
public void WriteDirectory(string directory, bool doRecursive)
{
if (string.IsNullOrEmpty(directory))
throw new ArgumentNullException(nameof(directory), "The directory is not set.");
WriteDirectoryEntry(directory, 0, 0, 0755);
string[] files = Directory.GetFiles(directory);
foreach (string fileName in files)
Write(fileName);
string[] directories = Directory.GetDirectories(directory);
foreach (string dirName in directories)
{
WriteDirectoryEntry(dirName, 0, 0, 0755);
if (doRecursive)
WriteDirectory(dirName, true);
}
}
/// <summary>
/// Writes a file.
/// </summary>
/// <param name="fileName">The file.</param>
/// <exception cref="ArgumentNullException"><paramref name="fileName"/> is not set.</exception>
public void Write(string fileName)
{
if (string.IsNullOrEmpty(fileName))
throw new ArgumentNullException(nameof(fileName), "The file name is not set.");
using var fileStream = File.OpenRead(fileName);
Write(fileStream, fileStream.Length, fileName, 61, 61, 511, File.GetLastWriteTime(fileStream.Name));
}
/// <summary>
/// Writes a file stream.
/// </summary>
/// <param name="file">The file stream.</param>
public void Write(FileStream file)
{
string path = Path.GetFullPath(file.Name).Replace(Path.GetPathRoot(file.Name), string.Empty);
path = path.Replace(Path.DirectorySeparatorChar, '/');
Write(file, file.Length, path, 61, 61, 511, File.GetLastWriteTime(file.Name));
}
/// <summary>
/// Writes a stream.
/// </summary>
/// <param name="data">The contents.</param>
/// <param name="dataSizeInBytes">The file size in bytes.</param>
/// <param name="name">The file name.</param>
public void Write(Stream data, long dataSizeInBytes, string name)
=> Write(data, dataSizeInBytes, name, 61, 61, 511, DateTime.Now);
/// <summary>
/// Writes a file to the archive.
/// </summary>
/// <param name="name">The file name.</param>
/// <param name="dataSizeInBytes">The file size in bytes.</param>
/// <param name="userId">The user id.</param>
/// <param name="groupId">The group id.</param>
/// <param name="mode">The access mode.</param>
/// <param name="lastModificationTime">The last modification timestamp.</param>
/// <param name="writeDelegate">The <see cref="WriteDataDelegate"/>.</param>
public virtual void Write(string name, long dataSizeInBytes, int userId, int groupId, int mode, DateTime lastModificationTime, WriteDataDelegate writeDelegate)
{
var writer = new DataWriter(OutStream, dataSizeInBytes);
WriteHeader(name, lastModificationTime, dataSizeInBytes, userId, groupId, mode, EntryType.File);
while (writer.CanWrite)
writeDelegate(writer);
AlignTo512(dataSizeInBytes, false);
}
/// <summary>
/// Writes a stream as file to the archive.
/// </summary>
/// <param name="data">The content as <see cref="Stream"/>.</param>
/// <param name="dataSizeInBytes">The file size in bytes.</param>
/// <param name="name">The file name.</param>
/// <param name="userId">The user id.</param>
/// <param name="groupId">The group id.</param>
/// <param name="mode">The access mode.</param>
/// <param name="lastModificationTime">The last modification timestamp.</param>
/// <exception cref="TarException">This writer is already closed.</exception>
public virtual void Write(Stream data, long dataSizeInBytes, string name, int userId, int groupId, int mode, DateTime lastModificationTime)
{
if (_isClosed)
throw new TarException("Can not write to the closed writer");
// handle long file names (> 99 characters)
if (name.Length > 99)
{
WriteLongName(
name: name,
userId: userId,
groupId: groupId,
mode: mode,
lastModificationTime: lastModificationTime);
// shorten the file name so it can be written properly
name = name.Substring(0, 99);
}
WriteHeader(name, lastModificationTime, dataSizeInBytes, userId, groupId, mode, EntryType.File);
WriteContent(dataSizeInBytes, data);
AlignTo512(dataSizeInBytes, false);
}
/// <summary>
/// Handle long file or path names (> 99 characters).
/// Write header and content, which its content contain the long (complete) file/path name.
/// <para>This handling method is adapted from https://github.com/qmfrederik/dotnet-packaging/pull/50/files#diff-f64c58cc18e8e445cee6ffed7a0d765cdb442c0ef21a3ed80bd20514057967b1 </para>
/// </summary>
/// <param name="name">File name or path name.</param>
/// <param name="userId">User ID.</param>
/// <param name="groupId">Group ID.</param>
/// <param name="mode">Mode.</param>
/// <param name="lastModificationTime">Last modification time.</param>
private void WriteLongName(string name, int userId, int groupId, int mode, DateTime lastModificationTime)
{
// must include a trailing \0
int nameLength = Encoding.UTF8.GetByteCount(name);
byte[] entryName = new byte[nameLength + 1];
Encoding.UTF8.GetBytes(name, 0, name.Length, entryName, 0);
// add a "././@LongLink" pseudo-entry which contains the full name
using var nameStream = new MemoryStream(entryName);
WriteHeader("././@LongLink", lastModificationTime, entryName.Length, userId, groupId, mode, EntryType.LongName);
WriteContent(entryName.Length, nameStream);
AlignTo512(entryName.Length, false);
}
/// <summary>
/// Writes a stream as file to the archive.
/// </summary>
/// <param name="count">The size of the file in bytes.</param>
/// <param name="data">The file content as stream.</param>
/// <exception cref="IOException"><paramref name="data"/> has not enough to read from.</exception>
protected void WriteContent(long count, Stream data)
{
while (count > 0 && count > buffer.Length)
{
int bytesRead = data.Read(buffer, 0, buffer.Length);
if (bytesRead < 0)
throw new IOException($"{nameof(LegacyTarWriter)} unable to read from provided stream");
if (bytesRead == 0)
{
if (ReadOnZero)
Thread.Sleep(100);
else
break;
}
OutStream.Write(buffer, 0, bytesRead);
count -= bytesRead;
}
if (count > 0)
{
int bytesRead = data.Read(buffer, 0, (int)count);
if (bytesRead < 0)
throw new IOException($"{nameof(LegacyTarWriter)} unable to read from provided stream");
if (bytesRead == 0)
{
while (count > 0)
{
OutStream.WriteByte(0);
--count;
}
}
else
OutStream.Write(buffer, 0, bytesRead);
}
}
/// <summary>
/// Writes a entry header to the archive.
/// </summary>
/// <param name="name">The file name.</param>
/// <param name="lastModificationTime">The last modification time.</param>
/// <param name="count">The number of bytes.</param>
/// <param name="userId">The user id.</param>
/// <param name="groupId">The group id.</param>
/// <param name="mode">The file mode.</param>
/// <param name="entryType">The entry type</param>
protected virtual void WriteHeader(string name, DateTime lastModificationTime, long count, int userId, int groupId, int mode, EntryType entryType)
{
var header = new TarHeader
{
FileName = name,
LastModification = lastModificationTime,
SizeInBytes = count,
UserId = userId,
GroupId = groupId,
Mode = mode,
EntryType = entryType
};
OutStream.Write(header.GetHeaderValue(), 0, header.HeaderSize);
}
/// <summary>
/// Aligns the entry to 512 bytes.
/// </summary>
public void AlignTo512(long size, bool acceptZero)
{
size %= 512;
if (size == 0 && !acceptZero) return;
while (size < 512)
{
OutStream.WriteByte(0);
size++;
}
}
/// <summary>
/// Closes the writer and aligns to 512 bytes.
/// </summary>
public virtual void Close()
{
if (_isClosed)
return;
AlignTo512(0, true);
AlignTo512(0, true);
_isClosed = true;
}
}
}

View File

@@ -1,52 +0,0 @@
using System;
namespace AMWD.Common.Packing.Tar.Utils
{
/// <summary>
/// Represents errors that occur during tar archive execution.
/// </summary>
public class TarException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="TarException"/> class.
/// </summary>
public TarException()
: base()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="TarException"/> class with a specified
/// error message.
/// </summary>
/// <param name="message">The message that describes the error.</param>
public TarException(string message)
: base(message)
{ }
/// <summary>
/// Initializes a new instance of the System.Exception class with a specified error
/// message and a reference to the inner exception that is the cause of this exception.
/// </summary>
/// <param name="message">The error message that explains the reason for the exception.</param>
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference
/// if no inner exception is specified.</param>
public TarException(string message, Exception innerException)
: base(message, innerException)
{ }
#if !NET8_0_OR_GREATER
/// <summary>
/// Initializes a new instance of the <see cref="TarException"/> class with serialized data.
/// </summary>
/// <param name="info">The <see cref="System.Runtime.Serialization.SerializationInfo"/> that holds the serialized
/// object data about the exception being thrown.</param>
/// <param name="context">The <see cref="System.Runtime.Serialization.StreamingContext"/> that contains contextual information
/// about the source or destination.</param>
protected TarException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
: base(info, context)
{ }
#endif
}
}

View File

@@ -1,179 +0,0 @@
using System;
using System.Linq;
using System.Net;
using System.Text;
using AMWD.Common.Packing.Tar.Interfaces;
namespace AMWD.Common.Packing.Tar.Utils
{
internal class TarHeader : ITarHeader
{
private static readonly byte[] _spaces = Encoding.ASCII.GetBytes(" ");
private readonly byte[] _buffer = new byte[512];
private string _fileName;
private long _headerChecksum;
public TarHeader()
{
// Default values
Mode = 511; // 0777 dec
UserId = 61; // 101 dec
GroupId = 61; // 101 dec
}
public EntryType EntryType { get; set; }
public virtual string FileName
{
get => _fileName.Replace("\0", string.Empty);
set
{
if (value.Length > 100)
throw new TarException("A file name can not be more than 100 chars long");
_fileName = value;
}
}
public int Mode { get; set; }
public string ModeString
=> Convert.ToString(Mode, 8).PadLeft(7, '0');
public int UserId { get; set; }
public virtual string UserName
{
get => UserId.ToString();
set => UserId = int.Parse(value);
}
public string UserIdString
=> Convert.ToString(UserId, 8).PadLeft(7, '0');
public int GroupId { get; set; }
public virtual string GroupName
{
get => GroupId.ToString();
set => GroupId = int.Parse(value);
}
public string GroupIdString
=> Convert.ToString(GroupId, 8).PadLeft(7, '0');
public long SizeInBytes { get; set; }
public string SizeString
=> Convert.ToString(SizeInBytes, 8).PadLeft(11, '0');
public DateTime LastModification { get; set; }
public string LastModificationString
{
get
{
long unixTime = ((DateTimeOffset)DateTime.SpecifyKind(LastModification, DateTimeKind.Utc)).ToUnixTimeSeconds();
return Convert.ToString(unixTime, 8).PadLeft(11, '0');
}
}
public string HeaderChecksumString
=> Convert.ToString(_headerChecksum, 8).PadLeft(6, '0');
public virtual int HeaderSize => 512;
public byte[] GetBytes() => _buffer.ToArray();
public virtual bool UpdateHeaderFromBytes()
{
FileName = Encoding.ASCII.GetString(_buffer, 0, 100);
// Thanks to Shasha Alperocivh. Trimming nulls.
Mode = Convert.ToInt32(Encoding.ASCII.GetString(_buffer, 100, 7).Trim(), 8);
UserId = Convert.ToInt32(Encoding.ASCII.GetString(_buffer, 108, 7).Trim(), 8);
GroupId = Convert.ToInt32(Encoding.ASCII.GetString(_buffer, 116, 7).Trim(), 8);
EntryType = (EntryType)_buffer[156];
if ((_buffer[124] & 0x80) == 0x80) // if size in binary
{
long sizeBigEndian = BitConverter.ToInt64(_buffer, 0x80);
SizeInBytes = IPAddress.NetworkToHostOrder(sizeBigEndian);
}
else
{
SizeInBytes = Convert.ToInt64(Encoding.ASCII.GetString(_buffer, 124, 11), 8);
}
long unixTimeStamp = Convert.ToInt64(Encoding.ASCII.GetString(_buffer, 136, 11), 8);
LastModification = DateTimeOffset.FromUnixTimeSeconds(unixTimeStamp).DateTime;
int storedChecksum = Convert.ToInt32(Encoding.ASCII.GetString(_buffer, 148, 6));
RecalculateChecksum(_buffer);
if (storedChecksum == _headerChecksum)
return true;
RecalculateAltChecksum(_buffer);
return storedChecksum == _headerChecksum;
}
private void RecalculateAltChecksum(byte[] buf)
{
_spaces.CopyTo(buf, 148);
_headerChecksum = 0;
foreach (byte b in buf)
{
if ((b & 0x80) == 0x80)
{
_headerChecksum -= b ^ 0x80;
}
else
{
_headerChecksum += b;
}
}
}
public virtual byte[] GetHeaderValue()
{
// Clean old values
Array.Clear(_buffer, 0, _buffer.Length);
if (string.IsNullOrEmpty(FileName))
throw new TarException("FileName can not be empty.");
if (FileName.Length >= 100)
throw new TarException("FileName is too long. It must be less than 100 bytes.");
// Fill header
Encoding.ASCII.GetBytes(FileName.PadRight(100, '\0')).CopyTo(_buffer, 0);
Encoding.ASCII.GetBytes(ModeString).CopyTo(_buffer, 100);
Encoding.ASCII.GetBytes(UserIdString).CopyTo(_buffer, 108);
Encoding.ASCII.GetBytes(GroupIdString).CopyTo(_buffer, 116);
Encoding.ASCII.GetBytes(SizeString).CopyTo(_buffer, 124);
Encoding.ASCII.GetBytes(LastModificationString).CopyTo(_buffer, 136);
// buffer[156] = 20;
_buffer[156] = ((byte)EntryType);
RecalculateChecksum(_buffer);
// Write checksum
Encoding.ASCII.GetBytes(HeaderChecksumString).CopyTo(_buffer, 148);
return _buffer;
}
protected virtual void RecalculateChecksum(byte[] buf)
{
// Set default value for checksum. That is 8 spaces.
_spaces.CopyTo(buf, 148);
// Calculate checksum
_headerChecksum = 0;
foreach (byte b in buf)
_headerChecksum += b;
}
}
}

View File

@@ -1,127 +0,0 @@
using System;
using System.Net;
using System.Text;
namespace AMWD.Common.Packing.Tar.Utils
{
/// <summary>
/// UsTar header implementation.
/// </summary>
internal class UsTarHeader : TarHeader
{
private const string Magic = "ustar";
private const string Version = " ";
private string _groupName;
private string _namePrefix = string.Empty;
private string _userName;
public override string UserName
{
get => _userName.Replace("\0", string.Empty);
set
{
if (value.Length > 32)
throw new TarException("user name can not be longer than 32 chars");
_userName = value;
}
}
public override string GroupName
{
get => _groupName.Replace("\0", string.Empty);
set
{
if (value.Length > 32)
throw new TarException("group name can not be longer than 32 chars");
_groupName = value;
}
}
public override string FileName
{
get => _namePrefix.Replace("\0", string.Empty) + base.FileName.Replace("\0", string.Empty);
set
{
if (value.Length > 100)
{
if (value.Length > 255)
throw new TarException("UsTar fileName can not be longer thatn 255 chars");
int position = value.Length - 100;
// Find first path separator in the remaining 100 chars of the file name
while (!IsPathSeparator(value[position]))
{
++position;
if (position == value.Length)
{
break;
}
}
if (position == value.Length)
position = value.Length - 100;
_namePrefix = value.Substring(0, position);
base.FileName = value.Substring(position, value.Length - position);
}
else
{
base.FileName = value;
}
}
}
public override bool UpdateHeaderFromBytes()
{
byte[] bytes = GetBytes();
UserName = Encoding.ASCII.GetString(bytes, 0x109, 32);
GroupName = Encoding.ASCII.GetString(bytes, 0x129, 32);
_namePrefix = Encoding.ASCII.GetString(bytes, 347, 157);
return base.UpdateHeaderFromBytes();
}
internal static bool IsPathSeparator(char ch)
=> ch == '\\' || ch == '/' || ch == '|'; // All the path separators I ever met.
public override byte[] GetHeaderValue()
{
byte[] header = base.GetHeaderValue();
Encoding.ASCII.GetBytes(Magic).CopyTo(header, 0x101); // Mark header as ustar
Encoding.ASCII.GetBytes(Version).CopyTo(header, 0x106);
Encoding.ASCII.GetBytes(UserName).CopyTo(header, 0x109);
Encoding.ASCII.GetBytes(GroupName).CopyTo(header, 0x129);
Encoding.ASCII.GetBytes(_namePrefix).CopyTo(header, 347);
if (SizeInBytes >= 0x1FFFFFFFF)
{
byte[] bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(SizeInBytes));
SetMarker(AlignTo12(bytes)).CopyTo(header, 124);
}
RecalculateChecksum(header);
Encoding.ASCII.GetBytes(HeaderChecksumString).CopyTo(header, 148);
return header;
}
private static byte[] SetMarker(byte[] bytes)
{
bytes[0] |= 0x80;
return bytes;
}
private static byte[] AlignTo12(byte[] bytes)
{
byte[] retVal = new byte[12];
bytes.CopyTo(retVal, 12 - bytes.Length);
return retVal;
}
}
}

View File

@@ -2,7 +2,7 @@
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Upcoming - 0000-00-00
@@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Solution restructured to use multiple test projects
- Optimized for C# 12
- `IPNetwork` is used from (as `Microsoft.AspNetCore.HttpOverrides` is tagged as "out of support"):
- .NET Standard 2.0 and .NET 6.0: `Microsoft.AspNetCore.HttpOverrides.IPNetwork`

View File

@@ -3,23 +3,31 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common", "AMWD.Common\AMWD.Common.csproj", "{F512C474-B670-4E47-911E-7C0674AA8E7E}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F2C7556A-99EB-43EB-8954-56A24AFE928F}"
ProjectSection(SolutionItems) = preProject
src\Directory.Build.props = src\Directory.Build.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.AspNetCore", "AMWD.Common.AspNetCore\AMWD.Common.AspNetCore.csproj", "{725F40C9-8172-487F-B3D0-D7E38B4DB197}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E5DF156A-6C8B-4004-BA4C-A8DDE6FD3ECD}"
ProjectSection(SolutionItems) = preProject
test\Directory.Build.props = test\Directory.Build.props
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.EntityFrameworkCore", "AMWD.Common.EntityFrameworkCore\AMWD.Common.EntityFrameworkCore.csproj", "{7091CECF-C981-4FB9-9CC6-91C4E65A6356}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common", "src\AMWD.Common\AMWD.Common.csproj", "{F512C474-B670-4E47-911E-7C0674AA8E7E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.AspNetCore", "src\AMWD.Common.AspNetCore\AMWD.Common.AspNetCore.csproj", "{725F40C9-8172-487F-B3D0-D7E38B4DB197}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.EntityFrameworkCore", "src\AMWD.Common.EntityFrameworkCore\AMWD.Common.EntityFrameworkCore.csproj", "{7091CECF-C981-4FB9-9CC6-91C4E65A6356}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.MessagePack", "src\AMWD.Common.MessagePack\AMWD.Common.MessagePack.csproj", "{EA014C15-93B6-4F2C-8229-1C13E22BF84A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.Test", "src\AMWD.Common.Test\AMWD.Common.Test.csproj", "{6EBA2792-0B66-4C90-89A1-4E1D26D16443}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.Tests", "test\AMWD.Common.Tests\AMWD.Common.Tests.csproj", "{9469D87B-126E-4338-92E3-701F762CB54D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F2C7556A-99EB-43EB-8954-56A24AFE928F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E5DF156A-6C8B-4004-BA4C-A8DDE6FD3ECD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AMWD.Common.Test", "AMWD.Common.Test\AMWD.Common.Test.csproj", "{6EBA2792-0B66-4C90-89A1-4E1D26D16443}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests", "UnitTests\UnitTests.csproj", "{9469D87B-126E-4338-92E3-701F762CB54D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{86DE1B7C-3ECF-49B1-AB28-A976A3973FF5}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{86DE1B7C-3ECF-49B1-AB28-A976A3973FF5}"
ProjectSection(SolutionItems) = preProject
CHANGELOG.md = CHANGELOG.md
icon.png = icon.png
@@ -27,13 +35,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{86DE1B7C-3
README.md = README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{7196DA2B-D858-4B25-BC23-865175CFCDEC}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{7196DA2B-D858-4B25-BC23-865175CFCDEC}"
ProjectSection(SolutionItems) = preProject
.gitlab-ci.yml = .gitlab-ci.yml
directory.build.props = directory.build.props
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utils", "Utils", "{93EC8B16-7DEF-4E39-B590-E804DEF7C607}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{93EC8B16-7DEF-4E39-B590-E804DEF7C607}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.gitignore = .gitignore
@@ -41,7 +49,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utils", "Utils", "{93EC8B16
nuget.config = nuget.config
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AMWD.Common.MessagePack", "AMWD.Common.MessagePack\AMWD.Common.MessagePack.csproj", "{EA014C15-93B6-4F2C-8229-1C13E22BF84A}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AMWD.Common.AspNetCore.Tests", "test\AMWD.Common.AspNetCore.Tests\AMWD.Common.AspNetCore.Tests.csproj", "{2CDA58C2-DE85-478D-9474-A937A20BECD1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -61,6 +69,10 @@ Global
{7091CECF-C981-4FB9-9CC6-91C4E65A6356}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7091CECF-C981-4FB9-9CC6-91C4E65A6356}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7091CECF-C981-4FB9-9CC6-91C4E65A6356}.Release|Any CPU.Build.0 = Release|Any CPU
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Release|Any CPU.Build.0 = Release|Any CPU
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6EBA2792-0B66-4C90-89A1-4E1D26D16443}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -69,10 +81,10 @@ Global
{9469D87B-126E-4338-92E3-701F762CB54D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9469D87B-126E-4338-92E3-701F762CB54D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9469D87B-126E-4338-92E3-701F762CB54D}.Release|Any CPU.Build.0 = Release|Any CPU
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA014C15-93B6-4F2C-8229-1C13E22BF84A}.Release|Any CPU.Build.0 = Release|Any CPU
{2CDA58C2-DE85-478D-9474-A937A20BECD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2CDA58C2-DE85-478D-9474-A937A20BECD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2CDA58C2-DE85-478D-9474-A937A20BECD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2CDA58C2-DE85-478D-9474-A937A20BECD1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -81,12 +93,13 @@ Global
{F512C474-B670-4E47-911E-7C0674AA8E7E} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
{725F40C9-8172-487F-B3D0-D7E38B4DB197} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
{7091CECF-C981-4FB9-9CC6-91C4E65A6356} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
{EA014C15-93B6-4F2C-8229-1C13E22BF84A} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
{6EBA2792-0B66-4C90-89A1-4E1D26D16443} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
{9469D87B-126E-4338-92E3-701F762CB54D} = {E5DF156A-6C8B-4004-BA4C-A8DDE6FD3ECD}
{86DE1B7C-3ECF-49B1-AB28-A976A3973FF5} = {AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB}
{7196DA2B-D858-4B25-BC23-865175CFCDEC} = {AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB}
{93EC8B16-7DEF-4E39-B590-E804DEF7C607} = {AFBF83AE-FE7D-48C1-B7E7-31BF3E17C6FB}
{EA014C15-93B6-4F2C-8229-1C13E22BF84A} = {F2C7556A-99EB-43EB-8954-56A24AFE928F}
{2CDA58C2-DE85-478D-9474-A937A20BECD1} = {E5DF156A-6C8B-4004-BA4C-A8DDE6FD3ECD}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {961E8DF8-DDF5-4D10-A510-CE409E9962AC}

View File

@@ -1,41 +1,11 @@
<Project>
<PropertyGroup>
<NrtRevisionFormat>{semvertag:main}{!:-dev}</NrtRevisionFormat>
<LangVersion>12.0</LangVersion>
<AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath>
<CopyRefAssembliesToPublishDirectory>false</CopyRefAssembliesToPublishDirectory>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<EmbedUntrackedSources>false</EmbedUntrackedSources>
<PackageProjectUrl>https://wiki.am-wd.de/libs/common</PackageProjectUrl>
<Description>Library with classes and extensions used frequently on AM.WD projects.</Description>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://git.am-wd.de/AM.WD/common.git</RepositoryUrl>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<Company>AM.WD</Company>
<Authors>Andreas Müller</Authors>
<Copyright>© {copyright:2020-} AM.WD</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<PropertyGroup Condition="'$(CI)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
<ItemGroup Condition="'$(CI)' == 'true'">
<SourceLinkGitLabHost Include="$(CI_SERVER_HOST)" Version="$(CI_SERVER_VERSION)" />
<PackageReference Include="Microsoft.SourceLink.GitLab" Version="8.0.0" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AMWD.NetRevisionTask" Version="1.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -10,7 +10,7 @@ To save time, they are all packed to NuGet packages.
| AMWD.Common.EntityFrameworkCore | ![https://nuget.am-wd.de/packages/amwd.common.entityframeworkcore/](https://services.am-wd.de/nuget.php?package=AMWD.Common.EntityFrameworkCore) |
| AMWD.Common.MessagePack | ![https://nuget.am-wd.de/packages/amwd.common.messagepack/](https://services.am-wd.de/nuget.php?package=AMWD.Common.MessagePack) |
| AMWD.Common.Test | ![https://nuget.am-wd.de/packages/amwd.common.test/](https://services.am-wd.de/nuget.php?package=AMWD.Common.Test) |
| CI / CD | ![https://git.am-wd.de/AM.WD/common/-/pipelines](https://git.am-wd.de/AM.WD/common/badges/main/pipeline.svg?style=flat-square) ![https://git.am-wd.de/AM.WD/common/-/tree/main](https://git.am-wd.de/AM.WD/common/badges/main/coverage.svg?style=flat-square) |
| CI / CD | ![https://git.am-wd.de/am-wd/common/-/pipelines](https://git.am-wd.de/am-wd/common/badges/main/pipeline.svg?style=flat-square) ![https://git.am-wd.de/am-wd/common/-/tree/main](https://git.am-wd.de/am-wd/common/badges/main/coverage.svg?style=flat-square) |
## Documentation

View File

@@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnitTests.Common.Packing.Tar
{
internal class TarReaderTests
{
}
}

View File

@@ -1,29 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<LangVersion>12.0</LangVersion>
<IsPackable>false</IsPackable>
<CollectCoverage>true</CollectCoverage>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.msbuild" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="DNS" Version="7.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
<PackageReference Include="ReflectionMagic" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AMWD.Common.AspNetCore\AMWD.Common.AspNetCore.csproj" />
<ProjectReference Include="..\AMWD.Common\AMWD.Common.csproj" />
</ItemGroup>
</Project>

View File

@@ -2,25 +2,15 @@
<PropertyGroup>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<LangVersion>12.0</LangVersion>
<NrtTagMatch>asp/v[0-9]*</NrtTagMatch>
<AssemblyName>AMWD.Common.AspNetCore</AssemblyName>
<AssemblyName>amwd-common-aspnetcore</AssemblyName>
<RootNamespace>AMWD.Common.AspNetCore</RootNamespace>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<NrtTagMatch>asp/v[0-9]*</NrtTagMatch>
<PackageId>AMWD.Common.AspNetCore</PackageId>
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Product>AM.WD Common Library for ASP.NET Core</Product>
</PropertyGroup>
<ItemGroup>
<None Include="../icon.png" Pack="true" PackagePath="/" />
<None Include="../README.md" Pack="true" PackagePath="/" />
</ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />

View File

@@ -2,25 +2,15 @@
<PropertyGroup>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<LangVersion>12.0</LangVersion>
<NrtTagMatch>efc/v[0-9]*</NrtTagMatch>
<AssemblyName>AMWD.Common.EntityFrameworkCore</AssemblyName>
<AssemblyName>amwd-common-efcore</AssemblyName>
<RootNamespace>AMWD.Common.EntityFrameworkCore</RootNamespace>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<NrtTagMatch>efc/v[0-9]*</NrtTagMatch>
<PackageId>AMWD.Common.EntityFrameworkCore</PackageId>
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Product>AM.WD Common Library for EntityFramework Core</Product>
</PropertyGroup>
<ItemGroup>
<None Include="../icon.png" Pack="true" PackagePath="/" />
<None Include="../README.md" Pack="true" PackagePath="/" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.0" />

View File

@@ -2,27 +2,17 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0;net8.0</TargetFrameworks>
<LangVersion>12.0</LangVersion>
<NrtTagMatch>msgpack/v[0-9]*</NrtTagMatch>
<AssemblyName>AMWD.Common.MessagePack</AssemblyName>
<AssemblyName>amwd-common-msgpack</AssemblyName>
<RootNamespace>AMWD.Common.MessagePack</RootNamespace>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<NrtTagMatch>msgpack/v[0-9]*</NrtTagMatch>
<PackageId>AMWD.Common.MessagePack</PackageId>
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Product>AM.WD Common Library for MessagePack</Product>
</PropertyGroup>
<ItemGroup>
<None Include="../icon.png" Pack="true" PackagePath="/" />
<None Include="../README.md" Pack="true" PackagePath="/" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MessagePack" Version="2.5.140" />
<PackageReference Include="MessagePack" Version="2.5.168" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net8.0'">

View File

@@ -2,28 +2,18 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>12.0</LangVersion>
<NrtTagMatch>test/v[0-9]*</NrtTagMatch>
<AssemblyName>AMWD.Common.Test</AssemblyName>
<AssemblyName>amwd-common-test</AssemblyName>
<RootNamespace>AMWD.Common.Test</RootNamespace>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<NrtTagMatch>test/v[0-9]*</NrtTagMatch>
<PackageId>AMWD.Common.Test</PackageId>
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Product>AM.WD Common Library for Unit-Testing</Product>
</PropertyGroup>
<ItemGroup>
<None Include="../icon.png" Pack="true" PackagePath="/" />
<None Include="../README.md" Pack="true" PackagePath="/" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.4.3" />
</ItemGroup>
</Project>

View File

@@ -2,24 +2,14 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
<LangVersion>12.0</LangVersion>
<AssemblyName>AMWD.Common</AssemblyName>
<AssemblyName>amwd-common</AssemblyName>
<RootNamespace>AMWD.Common</RootNamespace>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageId>AMWD.Common</PackageId>
<PackageIcon>icon.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Product>AM.WD Common Library</Product>
</PropertyGroup>
<ItemGroup>
<None Include="../icon.png" Pack="true" PackagePath="/" />
<None Include="../README.md" Pack="true" PackagePath="/" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Unclassified.DeepConvert" Version="1.4.0" />

View File

@@ -41,7 +41,7 @@ namespace System
if (InvalidHexCharRegex().IsMatch(str))
yield break;
#else
if (Regex.IsMatch(str, "[^0-9a-fA-F]"))
if (Regex.IsMatch(str, "[^0-9a-fA-F]", RegexOptions.Compiled))
yield break;
#endif
@@ -180,18 +180,30 @@ namespace System
/// <returns><c>true</c> when the email address is valid, other wise <c>false</c>.</returns>
public static bool IsValidEmailAddress(this string emailAddress, IEnumerable<IPEndPoint> nameservers)
{
if (string.IsNullOrWhiteSpace(emailAddress))
return false;
try
{
var mailAddress = new MailAddress(emailAddress);
bool isValid = mailAddress.Address == emailAddress;
if (mailAddress.Address != emailAddress)
return false;
if (isValid && nameservers?.Any() == true)
#if NET8_0_OR_GREATER
if (!ValidEmailRegex().IsMatch(emailAddress))
return false;
#else
string emailRegex = @"^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$";
if (!Regex.IsMatch(emailAddress, emailRegex, RegexOptions.Compiled))
return false;
#endif
if (nameservers?.Any() == true)
{
var dnsClientType = Type.GetType("DNS.Client.DnsClient, DNS") ?? throw new DllNotFoundException("The DNS NuGet package is required: https://www.nuget.org/packages/DNS/7.0.0");
var recordTypeType = Type.GetType("DNS.Protocol.RecordType, DNS");
var resolveMethodInfo = dnsClientType.GetMethod("Resolve", [typeof(string), recordTypeType, typeof(CancellationToken)]);
bool exists = false;
foreach (var nameserver in nameservers)
{
object dnsClient = Activator.CreateInstance(dnsClientType, [nameserver]);
@@ -210,21 +222,13 @@ namespace System
foreach (object item in (list as IEnumerable))
{
int type = (int)item.GetType().GetProperty("Type").GetValue(item);
if (type == 15)
{
exists = true;
break;
}
if (type == 15) // MX found
return true;
}
if (exists)
break;
}
isValid &= exists;
}
return isValid;
return true;
}
catch
{
@@ -244,8 +248,11 @@ namespace System
=> sb.Append(value).Append(newLine);
#if NET8_0_OR_GREATER
[GeneratedRegex("[^0-9a-fA-F]")]
[GeneratedRegex("[^0-9a-fA-F]", RegexOptions.Compiled)]
private static partial Regex InvalidHexCharRegex();
[GeneratedRegex(@"^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$", RegexOptions.Compiled)]
private static partial Regex ValidEmailRegex();
#endif
}
}

View File

@@ -0,0 +1,4 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("AMWD.Common.Tests")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

View File

@@ -14,7 +14,7 @@ namespace AMWD.Common.Packing.Ar
// Source: http://en.wikipedia.org/wiki/Ar_%28Unix%29
private readonly Stream _inStream;
private readonly List<ArFileInfoExtended> _files = new();
private readonly List<ArFileInfoExtended> _files = [];
private readonly long _streamStartPosition;
/// <summary>

View File

@@ -64,7 +64,7 @@ namespace AMWD.Common.Packing.Ar
// Align to even offsets, pad with LF bytes
if ((_outStream.Position % 2) != 0)
{
byte[] bytes = new byte[] { 0x0A };
byte[] bytes = [0x0A];
_outStream.Write(bytes, 0, 1);
}
}
@@ -124,12 +124,12 @@ namespace AMWD.Common.Packing.Ar
// File size in bytes
if (fileSize < 0 || 10000000000 <= fileSize)
throw new ArgumentOutOfRangeException("Invalid file size."); // above 9.32 GB
throw new ArgumentOutOfRangeException(nameof(fileSize), "Invalid file size."); // above 9.32 GB
WriteAsciiString(fileSize.ToString().PadRight(10, ' '));
// File magic
byte[] bytes = new byte[] { 0x60, 0x0A };
byte[] bytes = [0x60, 0x0A];
_outStream.Write(bytes, 0, 2);
}

Some files were not shown because too many files have changed in this diff Show More