Quick update.
This commit is contained in:
parent
6d192adce7
commit
7fc92d1e66
@ -2,6 +2,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.VisualBasic;
|
||||
|
||||
namespace ReMime.Cli
|
||||
{
|
||||
@ -47,7 +49,10 @@ namespace ReMime.Cli
|
||||
[DoesNotReturn]
|
||||
private static void ListTypes()
|
||||
{
|
||||
foreach (MediaType type in MediaTypeResolver.KnownTypes)
|
||||
var list = MediaTypeResolver.KnownTypes.ToList();
|
||||
list.Sort();
|
||||
|
||||
foreach (MediaType type in list)
|
||||
{
|
||||
Console.WriteLine("{0}\t{1}", type.FullTypeNoParameters, string.Join(' ', type.Extensions));
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
<!--NuGet-->
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<PackageId>ReFuel.ReMime.ReFile</PackageId>
|
||||
<Version>0.1.0</Version>
|
||||
<Version>0.1.1</Version>
|
||||
<Authors>H. Utku Maden</Authors>
|
||||
<Company>ReFuel</Company>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
@ -34,7 +34,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ReFuel.ReMime" Version="0.1.0" />
|
||||
<PackageReference Include="ReFuel.ReMime" Version="0.1.1" />
|
||||
|
||||
<Content Include="README.md" Pack="true" PackagePath="/" />
|
||||
<Content Include="../LICENSE.md" Pack="true" PackagePath="/" />
|
||||
|
@ -3,21 +3,9 @@ using ReMime.Platform;
|
||||
|
||||
namespace ReMime.Tests
|
||||
{
|
||||
public abstract class MediaTypesByExtension<T> where T : IMediaTypeResolver, new()
|
||||
[TestClass]
|
||||
public class MediaTypesByExtension
|
||||
{
|
||||
private T CIT;
|
||||
|
||||
protected MediaTypesByExtension()
|
||||
{
|
||||
Unsafe.SkipInit(out CIT);
|
||||
}
|
||||
|
||||
[TestInitialize]
|
||||
public virtual void Initialize()
|
||||
{
|
||||
CIT = new T();
|
||||
}
|
||||
|
||||
readonly (string extension, string type)[] ExampleMimeTypes = new (string, string)[] {
|
||||
("png", "image/png"),
|
||||
("gif", "image/gif"),
|
||||
@ -34,44 +22,10 @@ namespace ReMime.Tests
|
||||
{
|
||||
foreach (var(ext, type) in ExampleMimeTypes)
|
||||
{
|
||||
Assert.IsTrue(CIT.TryResolve(ext, out MediaType? result));
|
||||
Assert.IsTrue(MediaTypeResolver.TryResolve(ext, out MediaType? result));
|
||||
Assert.AreEqual(result!.FullType, type);
|
||||
Assert.IsTrue(result.Extensions.Contains(ext));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class UnixMediaTypes : MediaTypesByExtension<UnixMediaTypeResolver>
|
||||
{
|
||||
[TestInitialize]
|
||||
public override void Initialize()
|
||||
{
|
||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsFreeBSD())
|
||||
{
|
||||
base.Initialize();
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Inconclusive("Cannot test this in this platform.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class Win32MediaTypes : MediaTypesByExtension<Win32MediaTypeResolver>
|
||||
{
|
||||
[TestInitialize]
|
||||
public override void Initialize()
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
base.Initialize();
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.Inconclusive("Cannot test this in this platform.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
18
ReMime/ContentResolvers/IMagicValueResolver.cs
Normal file
18
ReMime/ContentResolvers/IMagicValueResolver.cs
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ReMime.ContentResolvers
|
||||
{
|
||||
public interface IMagicValueResolver
|
||||
{
|
||||
void AddMagicValue(MagicValueMediaType value);
|
||||
|
||||
void AddMagicValues(IEnumerable<MagicValueMediaType> values)
|
||||
{
|
||||
foreach (MagicValueMediaType value in values)
|
||||
{
|
||||
AddMagicValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
86
ReMime/ContentResolvers/MagEx.cs
Normal file
86
ReMime/ContentResolvers/MagEx.cs
Normal file
@ -0,0 +1,86 @@
|
||||
|
||||
using System;
|
||||
|
||||
namespace ReMime.ContentResolvers
|
||||
{
|
||||
/* You've heard of regular expressions, now prepare for magical expressions :sparkles:. */
|
||||
/// <summary>
|
||||
/// Bit pattern detecting state machine inspired by text regular expressions.
|
||||
/// </summary>
|
||||
public class MagEx
|
||||
{
|
||||
/**
|
||||
* 0 1 2 3 4 5 6 7 8 9 a b c d e f 4-bit patterns to match.
|
||||
* l h Single bit pattern.
|
||||
* * Any bit pattern.
|
||||
* ? Any 4-bit pattern.
|
||||
* 'pattern' ASCII pattern with no terminator. Implies @.
|
||||
* @ Align to 8-bits.
|
||||
* % Align to 4-bits.
|
||||
*/
|
||||
|
||||
public string Pattern { get; }
|
||||
|
||||
public MagEx(string pattern)
|
||||
{
|
||||
Pattern = pattern;
|
||||
}
|
||||
|
||||
public bool Match(ReadOnlySpan<byte> bytes)
|
||||
{
|
||||
byte current;
|
||||
int needle;
|
||||
int haystack;
|
||||
int bits;
|
||||
int pi = 0;
|
||||
ReadOnlySpan<char> ascii = ReadOnlySpan<char>.Empty;
|
||||
for (int i = 0; i < bytes.Length; i++)
|
||||
{
|
||||
current = bytes[i];
|
||||
bits = 8;
|
||||
|
||||
while (bits > 0)
|
||||
{
|
||||
char pat = Pattern[pi];
|
||||
switch (pat)
|
||||
{
|
||||
case '0': case '1': case '2': case '3':
|
||||
case '4': case '5': case '6': case '7':
|
||||
case '8': case '9': case 'a': case 'A':
|
||||
case 'b': case 'B': case 'c': case 'C':
|
||||
case 'd': case 'D': case 'e': case 'E':
|
||||
case 'f': case 'F':
|
||||
haystack = current & 0xF;
|
||||
current >>= 4;
|
||||
bits -= 4;
|
||||
|
||||
if (pat >= '0' && pat <= '9')
|
||||
{
|
||||
needle = pat - '0';
|
||||
}
|
||||
else if (pat >= 'a' && pat <= 'f')
|
||||
{
|
||||
needle = pat - 'a';
|
||||
}
|
||||
else
|
||||
{
|
||||
needle = pat - 'A';
|
||||
}
|
||||
|
||||
if (haystack == needle)
|
||||
{
|
||||
pi++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -6,30 +6,28 @@ using System.Linq;
|
||||
|
||||
namespace ReMime.ContentResolvers
|
||||
{
|
||||
public record MagicValueMediaType(MediaType MediaType, MagicValue[] MagicValues, string[] Extensions);
|
||||
public record MagicValueMediaType(MediaType MediaType, MagicValue[] MagicValues)
|
||||
{
|
||||
public IReadOnlyCollection<string> Extensions { get; } = MediaType.Extensions;
|
||||
}
|
||||
|
||||
public class MagicContentResolver : IMediaContentResolver
|
||||
public class MagicContentResolver : IMediaContentResolver, IMagicValueResolver
|
||||
{
|
||||
private readonly List<MediaType> _mediaTypes = new List<MediaType>();
|
||||
private readonly Dictionary<string, MediaType> _extensions = new Dictionary<string, MediaType>();
|
||||
private readonly Tree _tree = new Tree();
|
||||
private int _maxBytes = 0;
|
||||
|
||||
public MagicContentResolver(IEnumerable<MagicValueMediaType> values) : this()
|
||||
private MagicContentResolver()
|
||||
{
|
||||
AddMagicValues(values);
|
||||
}
|
||||
|
||||
public MagicContentResolver()
|
||||
{
|
||||
List<MagicValueDatabaseEntry> entries;
|
||||
IEnumerable<MagicValueMediaType> entries;
|
||||
|
||||
using (Stream str = typeof(MagicContentResolver).Assembly.GetManifestResourceStream("ReMime.ContentResolvers.database.jsonc")!)
|
||||
{
|
||||
entries = MagicValueDatabaseEntry.GetEntries(str);
|
||||
}
|
||||
|
||||
AddMagicValues(entries.Select(x => (MagicValueMediaType)x));
|
||||
AddMagicValues(entries);
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<MediaType> MediaTypes => _mediaTypes.AsReadOnly();
|
||||
@ -86,6 +84,8 @@ namespace ReMime.ContentResolvers
|
||||
return _extensions.TryGetValue(extension, out mediaType);
|
||||
}
|
||||
|
||||
public static MagicContentResolver Instance { get; } = new MagicContentResolver();
|
||||
|
||||
private class Tree
|
||||
{
|
||||
public MagicValueMediaType? Node { get; private set; }
|
@ -18,24 +18,24 @@ namespace ReMime.ContentResolvers
|
||||
[JsonPropertyName("extensions")]
|
||||
public List<string> Extensions { get; set; } = new List<string>();
|
||||
|
||||
public static List<MagicValueDatabaseEntry> GetEntries(Stream str)
|
||||
public static IEnumerable<MagicValueMediaType> GetEntries(Stream str)
|
||||
{
|
||||
return JsonSerializer.Deserialize<List<MagicValueDatabaseEntry>>(str, new JsonSerializerOptions()
|
||||
{
|
||||
AllowTrailingCommas = true,
|
||||
ReadCommentHandling = JsonCommentHandling.Skip
|
||||
}) ?? throw new Exception();
|
||||
})?.Select(x => (MagicValueMediaType)x)
|
||||
?? throw new Exception();
|
||||
}
|
||||
|
||||
public static explicit operator MagicValueMediaType(MagicValueDatabaseEntry entry)
|
||||
{
|
||||
return new MagicValueMediaType(
|
||||
new MediaType(entry.Type),
|
||||
new MediaType(entry.Type, entry.Extensions),
|
||||
entry.Magic.Select(x => (MagicValue.TryParse(x, out var value), value))
|
||||
.Where(x => x.Item1)
|
||||
.Select(x => (MagicValue)x.value!)
|
||||
.ToArray(),
|
||||
entry.Extensions.ToArray()
|
||||
.ToArray()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace ReMime.ContentResolvers
|
||||
{
|
||||
public class RiffResolver : IMediaTypeResolver, IMediaContentResolver
|
||||
public class RiffResolver : IMediaContentResolver, IMagicValueResolver
|
||||
{
|
||||
public readonly List<MediaType> _mediaTypes = new List<MediaType>();
|
||||
public readonly Dictionary<string, MediaType> _extensions = new Dictionary<string, MediaType>();
|
||||
@ -15,27 +15,26 @@ namespace ReMime.ContentResolvers
|
||||
|
||||
public IReadOnlyCollection<MediaType> MediaTypes { get; }
|
||||
|
||||
public RiffResolver()
|
||||
private RiffResolver()
|
||||
{
|
||||
MediaTypes = _mediaTypes.AsReadOnly();
|
||||
|
||||
List<MagicValueDatabaseEntry> entries;
|
||||
IEnumerable<MagicValueMediaType> entries;
|
||||
|
||||
using (Stream str = typeof(MagicContentResolver).Assembly.GetManifestResourceStream("ReMime.ContentResolvers.riff.jsonc")!)
|
||||
{
|
||||
entries = MagicValueDatabaseEntry.GetEntries(str);
|
||||
}
|
||||
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
AddRiffType((MagicValueMediaType)entry);
|
||||
}
|
||||
AddMagicValues(entries);
|
||||
}
|
||||
|
||||
public RiffResolver(IEnumerable<MagicValueMediaType> values) : this()
|
||||
public void AddMagicValues(IEnumerable<MagicValueMediaType> entries)
|
||||
{
|
||||
foreach (MagicValueMediaType value in values)
|
||||
AddRiffType(value);
|
||||
foreach (var entry in entries)
|
||||
{
|
||||
AddMagicValue(entry);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryResolve(Stream str, [NotNullWhen(true)] out MediaType? mediaType)
|
||||
@ -69,7 +68,7 @@ namespace ReMime.ContentResolvers
|
||||
/// Add a RIFF sub-magic value to this resolver.
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
public void AddRiffType(MagicValueMediaType type)
|
||||
public void AddMagicValue(MagicValueMediaType type)
|
||||
{
|
||||
if (type.MagicValues.Length == 0)
|
||||
throw new ArgumentException("Expected at least one media type.");
|
||||
@ -92,6 +91,8 @@ namespace ReMime.ContentResolvers
|
||||
}
|
||||
}
|
||||
|
||||
public static RiffResolver Instance { get; } = new RiffResolver();
|
||||
|
||||
[StructLayout(LayoutKind.Auto, Size = 12)]
|
||||
private struct RiffChunk
|
||||
{
|
||||
|
@ -50,22 +50,13 @@ namespace ReMime
|
||||
|
||||
static MediaTypeResolver()
|
||||
{
|
||||
AddResolver(new RiffResolver(), 9997);
|
||||
AddResolver(new MagicContentResolver(), 9998);
|
||||
AddResolver(RiffResolver.Instance, 1000);
|
||||
AddResolver(MagicContentResolver.Instance, 1001);
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
AddResolver(new Win32MediaTypeResolver());
|
||||
}
|
||||
else if (OperatingSystem.IsLinux())
|
||||
{
|
||||
AddResolver(new UnixMediaTypeResolver());
|
||||
// TODO: add freedesktop mime type database.
|
||||
}
|
||||
else if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
AddResolver(new UnixMediaTypeResolver()); //?
|
||||
}
|
||||
if (Win32MediaTypeResolver.Instance != null)
|
||||
AddResolver(Win32MediaTypeResolver.Instance, 1002);
|
||||
if (UnixMediaTypeResolver.Instance != null)
|
||||
AddResolver(UnixMediaTypeResolver.Instance, 1002);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace ReMime.Platform
|
||||
@ -13,7 +14,7 @@ namespace ReMime.Platform
|
||||
private readonly Dictionary<string, MediaType> _extensionsMap = new Dictionary<string, MediaType>();
|
||||
public IReadOnlyCollection<MediaType> MediaTypes { get; }
|
||||
|
||||
public UnixMediaTypeResolver()
|
||||
private UnixMediaTypeResolver()
|
||||
{
|
||||
{
|
||||
bool valid = OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsFreeBSD();
|
||||
@ -53,6 +54,20 @@ namespace ReMime.Platform
|
||||
return _extensionsMap.TryGetValue(extension, out mediaType);
|
||||
}
|
||||
|
||||
public static UnixMediaTypeResolver? Instance { get; } = null;
|
||||
|
||||
static UnixMediaTypeResolver()
|
||||
{
|
||||
try
|
||||
{
|
||||
Instance = new UnixMediaTypeResolver();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly char[] s_delimeters = new char[] { '\t', ' ' };
|
||||
|
||||
private static void DigestMimeDatabase(TextReader reader, List<MediaType> types)
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Microsoft.Win32;
|
||||
|
||||
@ -13,7 +14,7 @@ namespace ReMime.Platform
|
||||
private readonly Dictionary<string, MediaType> _extensionsMap = new Dictionary<string, MediaType>();
|
||||
public IReadOnlyCollection<MediaType> MediaTypes { get; }
|
||||
|
||||
public Win32MediaTypeResolver()
|
||||
private Win32MediaTypeResolver()
|
||||
{
|
||||
if (!OperatingSystem.IsWindows())
|
||||
throw new PlatformNotSupportedException();
|
||||
@ -57,5 +58,19 @@ namespace ReMime.Platform
|
||||
{
|
||||
return _extensionsMap.TryGetValue(extension, out mediaType);
|
||||
}
|
||||
|
||||
public static Win32MediaTypeResolver? Instance { get; } = null;
|
||||
|
||||
static Win32MediaTypeResolver()
|
||||
{
|
||||
try
|
||||
{
|
||||
Instance = new Win32MediaTypeResolver();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
<!--NuGet-->
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<PackageId>ReFuel.ReMime</PackageId>
|
||||
<Version>0.1.0</Version>
|
||||
<Version>0.1.1</Version>
|
||||
<Authors>H. Utku Maden</Authors>
|
||||
<Company>ReFuel</Company>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
|
Loading…
Reference in New Issue
Block a user