Implemented data: ser/des.

This commit is contained in:
H. Utku Maden 2025-10-12 20:23:56 +03:00
parent 10d912d302
commit e196d5f50f
4 changed files with 106 additions and 69 deletions

View File

@ -37,7 +37,7 @@ namespace ReFuel.Gltf
return File.OpenRead(path); return File.OpenRead(path);
} }
case "data": case "data":
return new GltfUriReader(Uri); return DataUriStream.FromUri(Uri.OriginalString);
default: default:
if (provider != null) if (provider != null)
return provider.OpenFile(pwd, Uri) ?? throw new FileNotFoundException(Uri.ToString()); return provider.OpenFile(pwd, Uri) ?? throw new FileNotFoundException(Uri.ToString());

View File

@ -33,7 +33,7 @@ namespace ReFuel.Gltf
internal GltfImage(GltfDocument document) : base(document) internal GltfImage(GltfDocument document) : base(document)
{ {
} }
public Stream Open(string? pwd = null, IGltfStreamProvider? provider = null) public Stream Open(string? pwd = null, IGltfStreamProvider? provider = null)
{ {
if (Uri == null) if (Uri == null)
@ -52,16 +52,16 @@ namespace ReFuel.Gltf
} }
else else
{ {
string path = Path.Combine(pwd ?? string.Empty, Uri.LocalPath); string path = Path.Combine(pwd ?? string.Empty, Uri.LocalPath);
return File.OpenRead(path); return File.OpenRead(path);
} }
case "data": case "data":
return new GltfUriReader(Uri); return DataUriStream.FromUri(Uri.OriginalString);
} }
} }
// otherwise // otherwise
if (provider != null) if (provider != null)
return provider.OpenFile(pwd, Uri) ?? throw new FileNotFoundException(Uri.ToString()); return provider.OpenFile(pwd, Uri) ?? throw new FileNotFoundException(Uri.ToString());
else else
@ -102,4 +102,4 @@ namespace ReFuel.Gltf
throw new NotImplementedException(); throw new NotImplementedException();
} }
} }
} }

99
ReFuel.Gltf/IO/DataUriStream.cs Executable file
View File

@ -0,0 +1,99 @@
using System.Text;
namespace ReFuel.Gltf.IO
{
public class DataUriStream : MemoryStream
{
public string? MediaType { get; set; }
public DataUriStream()
{
}
public DataUriStream(byte[] array) : base(array, true)
{
}
public DataUriStream(int capacity) : base(capacity)
{
}
public string ToUri(Encoding? encoding = null)
{
// Not the most efficient implementation here but it works.
StringBuilder builder = new StringBuilder((int)Length * 4 / 3);
ReadOnlySpan<byte> buffer = GetBuffer().AsSpan()[..(int)Length];
if (MediaType != null)
{
builder.Append(MediaType);
}
if (encoding == null)
{
builder.Append(";base64,");
builder.Append(Convert.ToBase64String(buffer));
}
else
{
builder.Append(';');
builder.Append(encoding.WebName);
builder.Append(',');
builder.Append(Uri.EscapeDataString(encoding.GetString(buffer)));
}
return builder.ToString();
}
public static DataUriStream FromUri(ReadOnlySpan<char> uri)
{
if (uri.CompareTo("data:", StringComparison.InvariantCulture) != 0)
{
throw new Exception("Expected a data uri.");
}
int commaIndex = uri.IndexOf(',');
string? mediaType = null;
string dataType = "base64";
ReadOnlySpan<char> data;
if (commaIndex != -1)
{
ReadOnlySpan<char> header = uri[5..commaIndex];
int lastSemicolon = header.LastIndexOf(';');
ReadOnlySpan<char> mediaTypeSpan = header[..lastSemicolon];
ReadOnlySpan<char> dataTypeSpan = header[(lastSemicolon + 1)..];
mediaType = mediaTypeSpan.ToString();
dataType = dataTypeSpan.ToString();
data = uri[(commaIndex + 1)..];
}
else
{
data = uri[5..];
}
DataUriStream stream;
if (dataType == "base64")
{
stream = new DataUriStream(data.Length * 3 / 4 + 1) { MediaType = mediaType };
if (!Convert.TryFromBase64Chars(data, stream.GetBuffer(), out int bytesWritten))
{
throw new Exception("Base64 conversion failed.");
}
stream.SetLength(bytesWritten);
}
else
{
Encoding encoding = Encoding.GetEncoding(dataType);
stream = new DataUriStream(encoding.GetBytes(Uri.UnescapeDataString(data.ToString())));
}
return stream;
}
}
}

View File

@ -1,62 +0,0 @@
namespace ReFuel.Gltf.IO
{
public class GltfUriReader : Stream
{
public Uri Uri { get; }
public string Type { get; }
public string Encoding { get; }
private string data;
private int index = 0;
public GltfUriReader(Uri uri)
{
if (uri.Scheme != "data")
throw new Exception("Expected a URI with 'data:' scheme.");
Uri = uri;
data = uri.AbsolutePath;
index = data.IndexOf(',') + 1;
if (index == 0)
{
throw new Exception("No data string.");
}
}
public override bool CanRead => true;
public override bool CanSeek => true;
public override bool CanWrite => false;
public override long Length => throw new NotImplementedException();
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public override void Flush()
{
throw new NotSupportedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
}
}