I want to read out the header of a file and compare it to a given signature. The start of the signature can have an offset.
This is my current function:
public FileTypeVerifyResult Verify(Stream stream)
{
stream.Position = 0;
var reader = new BinaryReader(stream);
var headerBytes = reader.ReadBytes(SignatureLength this.OffSet);
return new FileTypeVerifyResult
{
Name = Name,
Description = Description,
IsVerified = Signatures.Any(signature =>
headerBytes.Skip(this.OffSet).Take(signature.Length)
.SequenceEqual(signature)
)
};
}
This currently works with one offset but extensions exists that can have multiple offsets. So my first thought was to make the OffSet
property an int[]
with all offsets but then I don't know if this can be easily build into this linq expression.
Also, the file can have the offset any
(or any other value that means anywhere in the file like just -1
). How could such a thing build in?
CodePudding user response:
To be able to handle more offsets, this is my solution:
protected List<int> OffSets { get; set; }
public int OffSetLength => OffSets.Max(m => m);
public FileTypeVerifyResult Verify(Stream stream)
{
stream.Position = 0;
var reader = new BinaryReader(stream);
var headerBytes = reader.ReadBytes(SignatureLength OffSetLength);
reader.Close();
return new FileTypeVerifyResult
{
Name = Name,
Description = Description,
IsVerified = Signatures.Any(signature =>
OffSets.Any(offSet =>
headerBytes.Skip(offSet).Take(signature.Length)
.SequenceEqual(signature))
)
};
}
I don't think I can get the any/-1 offset in this function without rewriting everything.
CodePudding user response:
I think this is the case where a swiss knife is not what you want, Linq is good for many things, but it really doesn't add any value to the problem at hand IMHO because writing such a query will make ones eyeballs bleed, but here is my take at the problem itself with a sparse use of LINQ if at all :)
I have allowed myself to deduct missing items that were not supplied and rationalize naming a bit.
public class FileType
{
public class FileTypeVerifyResult
{
public string Name { get; set; }
public string Description { get; set; }
public bool IsVerified { get; set; }
}
public class Signature
{
/// <summary>
/// Actual signature
/// </summary>
public byte[] Payload { get; set; }
public int Length { get => Payload.Length; }
public List<int> OffSets { get; set; }
}
public string Name { get; set; }
public string Description { get; set; }
public List<Signature> Signatures { get; set; }
public FileTypeVerifyResult Verify(Stream stream)
{
if (stream == null) throw new ArgumentException(nameof(stream));
if(stream.CanSeek)
stream.Position = 0;
else
//TODO: Consider if we need this: throw new ArgumentException("Stream does not support seek", nameof(stream));
var bytes = new byte[stream.Length];
int wasRead = stream.Read(bytes, 0, (int)stream.Length);
if (wasRead == 0) throw new ArgumentException("Unable to read from stream");
foreach (var signature in Signatures)
{
foreach(var offset in signature.OffSets)
{
if(offset == -1)
{
//TODO: read signature length from each position first 0, then 1 until what is left is less than length of signature and if it fits
return createResultObject(true);
}
var candidateBytes = new byte[signature.Length];
candidateBytes = bytes.Skip(offset).Take(signature.Length).ToArray();
// I'd not use linq, instead Array.Copy(bytes, offset, candidateBytes, 0, signature.Length);
if(signature.Payload.Equals(candidateBytes))
{
return createResultObject(true);
}
}
}
return createResultObject();
FileTypeVerifyResult createResultObject(bool isValid = false)
{
return new FileTypeVerifyResult
{
Name = Name,
Description = Description,
IsVerified = isValid
};
}
}
}