public sealed class TlsDirectoryParser
{
    public TlsDirectoryInfo? Parse(byte[] rawBytes, NtHeaders nt, IReadOnlyList<SectionInfo> sections)
    {
        if (nt.OptionalHeader.DataDirectories.Count <= 9)
        {
            return null;
        }

        var dir = nt.OptionalHeader.DataDirectories[9];
        if (dir.VirtualAddress == 0 || dir.Size == 0)
        {
            return null;
        }

        var offset = RvaMapper.RvaToFileOffset(dir.VirtualAddress, sections, nt.OptionalHeader.SizeOfHeaders);
        if (offset is null)
        {
            return null;
        }

        var pos = offset.Value;
        if (!nt.OptionalHeader.IsPe32Plus)
        {
            BoundsChecker.EnsureRange(pos, 24, rawBytes.Length, "TLS directory");
            return new TlsDirectoryInfo(
                BitConverter.ToUInt32(rawBytes, pos),
                BitConverter.ToUInt32(rawBytes, pos + 4),
                BitConverter.ToUInt32(rawBytes, pos + 8),
                BitConverter.ToUInt32(rawBytes, pos + 12),
                BitConverter.ToUInt32(rawBytes, pos + 16),
                BitConverter.ToUInt32(rawBytes, pos + 20));
        }

        BoundsChecker.EnsureRange(pos, 40, rawBytes.Length, "TLS directory");
        return new TlsDirectoryInfo(
            BitConverter.ToUInt64(rawBytes, pos),
            BitConverter.ToUInt64(rawBytes, pos + 8),
            BitConverter.ToUInt64(rawBytes, pos + 16),
            BitConverter.ToUInt64(rawBytes, pos + 24),
            BitConverter.ToUInt32(rawBytes, pos + 32),
            BitConverter.ToUInt32(rawBytes, pos + 36));
    }
}