Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
4452d0f
Added error handling cases to functions
zbalkan Dec 5, 2025
63db9b4
Added first unit tests
zbalkan Dec 5, 2025
aa0dab0
Added BinaryNumber unit tests
zbalkan Dec 5, 2025
f7ec3c4
Added size check to BinaryNumber ctor
zbalkan Dec 5, 2025
c8fff7a
Added unit tests for BinaryNumber
zbalkan Dec 5, 2025
bc05ec3
Added null-check to TryQueueTask
zbalkan Dec 5, 2025
042f836
Unit tests for TaskPool
zbalkan Dec 5, 2025
a6dc0a9
Added tests for IndependentTaskScheduler class
zbalkan Dec 5, 2025
f1af538
USed .NET 9.0
zbalkan Dec 5, 2025
ac2b92a
Added null checks for collection extensions
zbalkan Dec 5, 2025
b7d779a
Added tests for CollectionExtensions
zbalkan Dec 5, 2025
237e5d0
Minor improvement an supressions
zbalkan Dec 5, 2025
9b8120c
Moved tests under project-specific folder for cleaner structure.
zbalkan Dec 5, 2025
0d01eb5
Added unit tests for JsonExtensions class
zbalkan Dec 5, 2025
ee17507
Added unit tests for StringExtensions class
zbalkan Dec 5, 2025
604a640
Added tests for TaskExtensions class
zbalkan Dec 5, 2025
7f2a2e1
Rolled solution version back to VS2022 for compatibility
zbalkan Dec 6, 2025
52864ec
Response to review
zbalkan Dec 6, 2025
5f7abea
Added another edge case
zbalkan Dec 6, 2025
60f0333
Added folders for the projects to be covered
zbalkan Dec 6, 2025
5bbdcef
Added ByteTree tests
zbalkan Dec 6, 2025
ffba37b
Added binary stream size check
zbalkan Dec 6, 2025
46737f4
Added tests for BinaryReaderExtensions
zbalkan Dec 6, 2025
ce45c4f
Added tests for BinaryWriterExtensions
zbalkan Dec 6, 2025
33d4857
Improve disposal behavior in Joint to guarantee both stream copies co…
zbalkan Dec 6, 2025
b553de0
Added tests for Joint class
zbalkan Dec 6, 2025
abdf660
Added tests for OffsetStream class
zbalkan Dec 6, 2025
f433561
Added tests for using Package class
zbalkan Dec 6, 2025
41e4613
Corrected OffsetStream creation order so parsed items reference the c…
zbalkan Dec 7, 2025
119405d
Wrote tests for PackageItem class and modified PackageTests related …
zbalkan Dec 7, 2025
b7cdbbf
Added unit tests for Pipe class
zbalkan Dec 7, 2025
180fb34
Tests fo StreamExtensions class
zbalkan Dec 7, 2025
2c8af03
Tests for WriteBufferedStream class
zbalkan Dec 7, 2025
d77f674
Hardened Constructor_FromFilePath_ShouldCaptureAttributesAndOwnStream
zbalkan Dec 7, 2025
a815c48
Hardened Extract_ShouldBackupExisting_WhenOverwriteEnabled
zbalkan Dec 7, 2025
5af460b
Hardened Extract_ShouldNotOverwrite_WhenFlagDisabled
zbalkan Dec 7, 2025
9936b30
Hardened Dispose_ShouldCloseOwnedStream
zbalkan Dec 7, 2025
6dcb991
Hardened Constructor_FromFilePath_ShouldCaptureAttributesAndOwnStream
zbalkan Dec 7, 2025
531515c
Hardened WriteThenParse_WithCustomLocation_ShouldRoundtrip
zbalkan Dec 7, 2025
e59b5a4
Minor cleanup
zbalkan Dec 7, 2025
8d0050c
Minor cleanup
zbalkan Dec 7, 2025
824ecdd
Improved reliability on Extract_ShouldBackupExisting_WhenOverwriteEna…
zbalkan Dec 7, 2025
6797265
Improved async handling
zbalkan Dec 7, 2025
cad551b
Cleanup
zbalkan Dec 7, 2025
c9e72eb
Use explicit disposal in test to ensure disposal event is raised at t…
zbalkan Dec 7, 2025
79eb645
Simplified StreamOf
zbalkan Dec 7, 2025
a38ba53
Added DatetimeKind
zbalkan Dec 7, 2025
9203414
Formatting
zbalkan Dec 7, 2025
843cc9b
Marked private class sealed
zbalkan Dec 7, 2025
eb8b4aa
Removed duplicate test
zbalkan Dec 7, 2025
c870dcc
Added WindowsFirewall tests
zbalkan Dec 7, 2025
b4cfd82
Added tests for OTP
zbalkan Dec 7, 2025
12d3a0b
Formatting
zbalkan Dec 7, 2025
f8d0dca
Added minor improvements and validations to Authenticator
zbalkan Dec 7, 2025
c3df12c
Aded tests for Authenticator
zbalkan Dec 7, 2025
6e1f25b
Added null, empty and whitespace checks for DomainEndPoint validation
zbalkan Dec 9, 2025
5e1ad36
Wrote tests for DomainEndPoint class
zbalkan Dec 9, 2025
8dea00d
Cleaned up unused usings
zbalkan Dec 9, 2025
39850a0
Fixed null handling. Null domain name returns false instead of except…
zbalkan Dec 9, 2025
42e6768
Added tests for IPAddressExtensions class
zbalkan Dec 9, 2025
f7b48e1
Simplified tests in ByteTreeTests
zbalkan Dec 9, 2025
3101bc3
Refactored GetNetworkAddress
zbalkan Dec 9, 2025
606a398
Fixed extra octet edge case with tests
zbalkan Dec 9, 2025
97c1d79
Added null-check to IsPrivateIP
zbalkan Dec 9, 2025
88f8976
Formatting
zbalkan Dec 9, 2025
8a7a31e
Added tests for NetUtilities class
zbalkan Dec 9, 2025
8284bb4
Improved TryParse with validations
zbalkan Dec 9, 2025
5018042
Added tests for EndPointExtensions class
zbalkan Dec 9, 2025
28e4bb6
Improved port validation
zbalkan Dec 9, 2025
0b92ce3
Added unit tests for NetworkAccessControl
zbalkan Dec 9, 2025
adff737
Added unit tests for NetworkAddress class
zbalkan Dec 9, 2025
1c2bb5d
Added unit tests and improved parsing for NetworkMap class
zbalkan Dec 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[*.cs]

# IDE0008: Use explicit type
dotnet_diagnostic.IDE0008.severity = silent

# IDE0300: Simplify collection initialization
dotnet_diagnostic.IDE0300.severity = silent
68 changes: 48 additions & 20 deletions TechnitiumLibrary.IO/BinaryReaderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,14 @@ public static class BinaryReaderExtensions
{
public static byte[] ReadBuffer(this BinaryReader bR)
{
return bR.ReadBytes(ReadLength(bR));
int len = ReadLength(bR);

byte[] buffer = bR.ReadBytes(len);

if (buffer.Length != len)
throw new EndOfStreamException("Unexpected end of stream while reading buffer.");

return buffer;
}

public static string ReadShortString(this BinaryReader bR)
Expand All @@ -38,32 +45,53 @@ public static string ReadShortString(this BinaryReader bR)

public static string ReadShortString(this BinaryReader bR, Encoding encoding)
{
return encoding.GetString(bR.ReadBytes(bR.ReadByte()));
int length = bR.ReadByte();
byte[] bytes = bR.ReadBytes(length);

if (bytes.Length != length)
throw new EndOfStreamException("Not enough bytes to read short string.");

return encoding.GetString(bytes);
}

public static DateTime ReadDateTime(this BinaryReader bR)
{
return DateTime.UnixEpoch.AddMilliseconds(bR.ReadInt64());
// Read int64 big-endian timestamp (same as original behavior because .NET native is LE)
Span<byte> buffer = stackalloc byte[8];
int read = bR.BaseStream.Read(buffer);

if (read != 8)
throw new EndOfStreamException("Not enough bytes to read DateTime ticks.");

long millis = BinaryPrimitives.ReadInt64LittleEndian(buffer);
return DateTime.UnixEpoch.AddMilliseconds(millis);
}

public static int ReadLength(this BinaryReader bR)
{
int length1 = bR.ReadByte();
if (length1 > 127)
{
int numberLenBytes = length1 & 0x7F;
if (numberLenBytes > 4)
throw new IOException("BinaryReaderExtension encoding length not supported.");

Span<byte> valueBytes = stackalloc byte[4];
bR.BaseStream.ReadExactly(valueBytes.Slice(4 - numberLenBytes, numberLenBytes));

return BinaryPrimitives.ReadInt32BigEndian(valueBytes);
}
else
{
return length1;
}
int first = bR.ReadByte();
if (first < 0)
throw new EndOfStreamException("Not enough bytes for a length prefix.");

// Single byte value
if (first <= 127)
return first;

// Otherwise, multi-byte length
int numberLenBytes = first & 0x7F;

if (numberLenBytes > 4)
throw new IOException("BinaryReaderExtension encoding length not supported.");

Span<byte> temp = stackalloc byte[4];

int offset = 4 - numberLenBytes;
int readBytes = bR.BaseStream.Read(temp[offset..]);

if (readBytes != numberLenBytes)
throw new EndOfStreamException("Not enough bytes for encoded length.");

return BinaryPrimitives.ReadInt32BigEndian(temp);
}
}
}
}
46 changes: 16 additions & 30 deletions TechnitiumLibrary.IO/Joint.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,6 @@
/*
Technitium Library
Copyright (C) 2024 Shreyas Zare (shreyas@technitium.com)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

*/

using System;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace TechnitiumLibrary.IO
Expand All @@ -36,6 +18,9 @@ public class Joint : IDisposable
readonly Stream _stream1;
readonly Stream _stream2;

// track copy completion
private int _pendingCopies = 2;

#endregion

#region constructor
Expand Down Expand Up @@ -72,11 +57,8 @@ protected virtual void Dispose(bool disposing)
{
Disposing?.Invoke(this, EventArgs.Empty);

if (_stream1 != null)
_stream1.Dispose();

if (_stream2 != null)
_stream2.Dispose();
_stream1?.Dispose();
_stream2?.Dispose();
}
}
}
Expand All @@ -85,6 +67,12 @@ protected virtual void Dispose(bool disposing)

#region private

private void OnCopyFinished()
{
if (Interlocked.Decrement(ref _pendingCopies) == 0)
Dispose();
}

private async Task CopyToAsync(Stream src, Stream dst)
{
try
Expand All @@ -93,7 +81,7 @@ private async Task CopyToAsync(Stream src, Stream dst)
}
finally
{
Dispose();
OnCopyFinished();
}
}

Expand All @@ -111,11 +99,9 @@ public void Start()

#region properties

public Stream Stream1
{ get { return _stream1; } }
public Stream Stream1 => _stream1;

public Stream Stream2
{ get { return _stream2; } }
public Stream Stream2 => _stream2;

#endregion
}
Expand Down
9 changes: 7 additions & 2 deletions TechnitiumLibrary.IO/PackageItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,14 @@ public static PackageItem Parse(Stream s)
item._extractToCustomLocation = Encoding.UTF8.GetString(bR.ReadBytes(bR.ReadByte()));

long length = bR.ReadInt64();
item._data = new OffsetStream(bR.BaseStream, bR.BaseStream.Position, length, true);

bR.BaseStream.Position += length;
long startOffset = bR.BaseStream.Position;

// Create slice before advancing stream pointer
item._data = new OffsetStream(bR.BaseStream, startOffset, length, readOnly: true);

// Seek explicitly
bR.BaseStream.Seek(length, SeekOrigin.Current);

return item;

Expand Down
6 changes: 6 additions & 0 deletions TechnitiumLibrary.Net/DomainEndPoint.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ private DomainEndPoint()

public static bool TryParse(string value, out DomainEndPoint ep)
{
if (string.IsNullOrEmpty(value) || string.IsNullOrWhiteSpace(value))
{
ep = null;
return false;
}

string[] parts = value.Split(':');
if (parts.Length > 2)
{
Expand Down
43 changes: 25 additions & 18 deletions TechnitiumLibrary.Net/EndPointExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,20 +181,33 @@ public static EndPoint GetEndPoint(string address, int port)

public static bool TryParse(string value, out EndPoint ep)
{
ep = null;
if (string.IsNullOrWhiteSpace(value))
return false;

// First handle IP:port
if (IPEndPoint.TryParse(value, out IPEndPoint ep1))
{
ep = ep1;
return true;
}

if (DomainEndPoint.TryParse(value, out DomainEndPoint ep2))
{
ep = ep2;
return true;
}
// Now handle domain:port
int idx = value.LastIndexOf(':');
if (idx <= 0) // must be >0 because first char cannot be colon
return false;

ep = null;
return false;
string host = value.Substring(0, idx);
string portText = value.Substring(idx + 1);

if (!int.TryParse(portText, out int port) || port < 0 || port > 65535)
return false;

if (!DomainEndPoint.TryParse(value, out DomainEndPoint ep2))
return false;

ep = ep2;
return true;
}

public static bool IsEquals(this EndPoint ep, EndPoint other)
Expand All @@ -208,18 +221,12 @@ public static bool IsEquals(this EndPoint ep, EndPoint other)
if (ep.AddressFamily != other.AddressFamily)
return false;

switch (ep.AddressFamily)
return ep.AddressFamily switch
{
case AddressFamily.InterNetwork:
case AddressFamily.InterNetworkV6:
return (ep as IPEndPoint).Equals(other);

case AddressFamily.Unspecified:
return (ep as DomainEndPoint).Equals(other);

default:
throw new NotSupportedException("Address Family not supported.");
}
AddressFamily.InterNetwork or AddressFamily.InterNetworkV6 => (ep as IPEndPoint).Equals(other),
AddressFamily.Unspecified => (ep as DomainEndPoint).Equals(other),
_ => throw new NotSupportedException("Address Family not supported."),
};
}

#endregion
Expand Down
Loading