• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JaCraig / Archivist / 20125087508

11 Dec 2025 07:15AM UTC coverage: 77.194% (-1.6%) from 78.836%
20125087508

push

github

web-flow
Merge pull request #234 from JaCraig/dependabot/nuget/Archivist.OCR.Tests/dependencies-406f96db61

chore: Bump the dependencies group with 1 update

2403 of 3412 branches covered (70.43%)

Branch coverage included in aggregate %.

3192 of 3836 relevant lines covered (83.21%)

5030.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.26
/Archivist/BaseClasses/ReaderBaseClass.cs
1
using Archivist.Interfaces;
2
using Microsoft.Extensions.Logging;
3
using System;
4
using System.Buffers;
5
using System.Diagnostics.CodeAnalysis;
6
using System.IO;
7
using System.Threading.Tasks;
8

9
namespace Archivist.BaseClasses
10
{
11
    /// <summary>
12
    /// Base class for format readers.
13
    /// </summary>
14
    /// <seealso cref="IFormatReader"/>
15
    /// <remarks>Initializes a new instance of the <see cref="ReaderBaseClass"/> class.</remarks>
16
    /// <param name="logger">The logger to use for logging.</param>
17
    public abstract class ReaderBaseClass(ILogger? logger) : IFormatReader
389✔
18
    {
19
        /// <summary>
20
        /// Gets the header information for the format.
21
        /// </summary>
22
        public abstract byte[] HeaderInfo { get; }
23

24
        /// <summary>
25
        /// Gets the logger to use for logging.
26
        /// </summary>
27
        protected ILogger? Logger { get; } = logger;
917✔
28

29
        /// <summary>
30
        /// Determines whether the reader can read the specified stream.
31
        /// </summary>
32
        /// <param name="stream">The stream to read.</param>
33
        /// <returns><c>true</c> if the reader can read the stream; otherwise, <c>false</c>.</returns>
34
        public bool CanRead(Stream? stream)
35
        {
36
            if (!IsValidStream(stream))
202✔
37
            {
38
                Logger?.LogDebug("{readerName}.CanRead(): Stream is null or invalid.", GetType().Name);
198!
39
                return false;
198✔
40
            }
41
            if (HeaderInfo.Length == 0)
4!
42
                return InternalCanRead(stream);
×
43
            try
44
            {
45
                _ = stream.Seek(0, SeekOrigin.Begin);
4✔
46
                var StartIndex = FindStartIndex(stream);
4✔
47
                _ = stream.Seek(StartIndex, SeekOrigin.Begin);
4✔
48
                var Buffer = ArrayPool<byte>.Shared.Rent(HeaderInfo.Length);
4✔
49
                _ = stream.Read(Buffer, 0, Buffer.Length);
4✔
50
                _ = stream.Seek(0, SeekOrigin.Begin);
4✔
51
                for (var X = 0; X < HeaderInfo.Length; ++X)
30✔
52
                {
53
                    if (Buffer[X] != HeaderInfo[X])
14✔
54
                    {
55
                        ArrayPool<byte>.Shared.Return(Buffer);
3✔
56
                        return false;
3✔
57
                    }
58
                }
59
                ArrayPool<byte>.Shared.Return(Buffer);
1✔
60
                return InternalCanRead(stream);
1✔
61
            }
62
            catch (Exception Ex)
×
63
            {
64
                Logger?.LogError(Ex, "{readerName}.CanRead(): Error occurred while checking if the reader can read the stream.", GetType().Name);
×
65
                return false;
×
66
            }
67
        }
4✔
68

69
        /// <summary>
70
        /// Used to determine if a reader can actually read the file.
71
        /// </summary>
72
        /// <param name="stream">The stream to read.</param>
73
        /// <returns><c>true</c> if the reader can read the file; otherwise, <c>false</c>.</returns>
74
        public virtual bool InternalCanRead([NotNullWhen(true)] Stream? stream) => true;
6✔
75

76
        /// <summary>
77
        /// Reads the file asynchronously.
78
        /// </summary>
79
        /// <param name="stream">The stream to read.</param>
80
        /// <returns>
81
        /// A task representing the asynchronous operation that returns the generic file.
82
        /// </returns>
83
        public abstract Task<IGenericFile?> ReadAsync(Stream? stream);
84

85
        /// <summary>
86
        /// Validates if the provided stream is readable, has a non-zero length, and supports seeking.
87
        /// </summary>
88
        /// <param name="stream">The stream to validate.</param>
89
        /// <returns><c>true</c> if the stream is valid; otherwise, <c>false</c>.</returns>
90
        protected static bool IsValidStream([NotNullWhen(true)] Stream? stream)
91
        {
92
            if (stream?.CanRead != true)
571✔
93
                return false;
59✔
94
            if (stream.Length == 0)
512✔
95
                return false;
18✔
96
            if (!stream.CanSeek)
494!
97
                return false;
×
98
            try
99
            {
100
                var TempBuffer = ArrayPool<byte>.Shared.Rent(1);
494✔
101
                _ = stream.Seek(0, SeekOrigin.Begin);
494✔
102
                _ = stream.Read(TempBuffer, 0, 1);
54✔
103
                _ = stream.Seek(0, SeekOrigin.Begin);
54✔
104
                ArrayPool<byte>.Shared.Return(TempBuffer);
54✔
105
            }
54✔
106
            catch
440✔
107
            {
108
                return false;
440✔
109
            }
110
            return true;
54✔
111
        }
440✔
112

113
        /// <summary>
114
        /// Finds the start index of the file.
115
        /// </summary>
116
        /// <param name="stream">The stream to read.</param>
117
        /// <returns>The start index of the file.</returns>
118
        private static int FindStartIndex(Stream stream)
119
        {
120
            if (stream.Length < 3)
4!
121
                return 0;
×
122
            var BOMBuffer = ArrayPool<byte>.Shared.Rent(3);
4✔
123
            _ = stream.Read(BOMBuffer, 0, BOMBuffer.Length);
4✔
124
            _ = stream.Seek(0, SeekOrigin.Begin);
4✔
125
            var ReturnValue = BOMBuffer[0] == 0xEF && BOMBuffer[1] == 0xBB && BOMBuffer[2] == 0xBF ? 3 : 0;
4!
126
            ArrayPool<byte>.Shared.Return(BOMBuffer);
4✔
127
            return ReturnValue;
4✔
128
        }
129
    }
130
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc