实现 IRandomAccessStream,而不是复制缓冲区
Implementing IRandomAccessStream, not copying buffers
我对 targetBuffer
在 ReadAsync()
实现中应该做什么感到有点困惑(适用于 win 8.1 的通用商店应用程序)。
public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer targetBuffer, uint count, InputStreamOptions options)
问题是,根据我的具体实施要求,我找不到写入 targetBuffer
和更改其 Length
的方法。
我在里面的是一个带有一些块密码的加密流。我想用 IRandomAccessStream 包装它,因此它可以与 xaml 框架组件一起使用( 例如将加密的 images/video 传递给 Image
或 MediaElement
对象).在 class 中,我有一个字节数组,我将其重复用于每个块,将其传递给填充它并报告块大小的加密库。
所以,当 IRandomAccessStream.ReadAsync()
被调用时,我需要以某种方式将我的字节放入 targetBuffer
并将其 Length
设置为正确的值......我似乎没有管理。
我试过这个:
var stream = targetBuffer.AsStream();
while(count > 0) {
/* doing something to get next chunk of data decrypted */
// byte[] chunk is the array used to hold decrypted data
// int chunkLength is the length of data (<= chunk.Length)
count -= chunkLength;
await stream.WriteAsync(chunk, 0, chunkLength);
}
return targetBuffer;
并且 targetBuffer.Length
保持为零,但如果我尝试打印其内容,数据就在那里!
Debug.WriteLine(targetBuffer.GetByte(0..N));
我现在有一个简单的实现,它使用内存流(除了字节数组缓冲区),在那里收集数据并从中读回 targetBuffer
。这有效,但看起来很糟糕。托管流写入 byte[]
,WinRT 流写入 IBuffer
,我只是找不到解决方法,以免浪费内存和性能。
如果有任何想法,我将不胜感激。
这是现在的样子。我最终使用字节数组作为解密缓冲区,使用可调整大小的内存流作为代理。
public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer targetBuffer, uint count, InputStreamOptions options)
{
return AsyncInfo.Run<IBuffer, uint>(async (token, progress) => {
Transport.Seek(0); // Transport is InMemoryRandomAccessStream
var remaining = count;
while(remaining > 0) {
/*
ReadAsync() overload reads & decrypts data,
result length is <= remaining bytes,
deals with block cipher alignment and the like
*/
IBuffer chunk = await ReadAsync(remaining);
await Transport.WriteAsync(chunk);
remaining -= chunk.Length;
}
Transport.Seek(0);
// copy resulting bytes to target buffer
await Transport.ReadAsync(targetBuffer, count, InputStreamOptions.None);
return targetBuffer;
});
}
更新: 我已经用 7.9Mb 的加密图像测试了上面的解决方案。我把它喂给了 Image
这样的实例:
var image = new BitmapImage();
await image.SetSourceAsync(myCustomStream);
Img.Source = image; // Img is <Image> in xaml
一切正常,直到执行达到 await Transport.ReadAsync(targetBuffer, count, InputStreamOptions.None);
:内存消耗猛增(从大约 33mb 到 300+mb),这实际上导致 phone 模拟器崩溃(桌面版显示图像正常,但内存不足)消耗相同)。到底是怎么回事?!
2017 年 3 月解决
首先,我不知何故没有意识到我可以在将数据写入缓冲区后直接设置 Length
。其次,如果你在我的案例中做错了什么(自定义 IRandomAccessStream
实现是 XAML Image
元素的来源),应用程序崩溃时不会留下任何日志也不会显示任何错误,所以真的很难弄清楚哪里出了问题。
这就是代码现在的样子:
public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer targetBuffer, uint count, InputStreamOptions options)
{
return AsyncInfo.Run<IBuffer, uint>(async (token, progress) => {
var output = targetBuffer.AsStream();
while (count > 0) {
//
// do all the decryption stuff and get decrypted data
// to a reusable buffer byte array
//
int bytes = Math.Min((int) count, BufferLength - BufferPosition);
output.Write(decrypted, bufferPosition, bytes);
targetBuffer.Length += (uint)bytes;
BufferPosition += bytes;
progress.Report((uint)bytes);
count -= (uint)bytes;
}
}
return targetBuffer;
});
使用 System.Runtime.InteropServices.WindowsRuntime;
(your byte array).CopyTo(targetBuffer);
IBuffer 中的长度 属性 有一个 setter
以下代码完全有效
targetBuffer.Length = (your integer here)
您有更多 CopyTo 变体可供选择。看看这个:
public static void CopyTo(this byte[] source, int sourceIndex, IBuffer destination, uint destinationIndex, int count);
我对 targetBuffer
在 ReadAsync()
实现中应该做什么感到有点困惑(适用于 win 8.1 的通用商店应用程序)。
public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer targetBuffer, uint count, InputStreamOptions options)
问题是,根据我的具体实施要求,我找不到写入 targetBuffer
和更改其 Length
的方法。
我在里面的是一个带有一些块密码的加密流。我想用 IRandomAccessStream 包装它,因此它可以与 xaml 框架组件一起使用( 例如将加密的 images/video 传递给 Image
或 MediaElement
对象).在 class 中,我有一个字节数组,我将其重复用于每个块,将其传递给填充它并报告块大小的加密库。
所以,当 IRandomAccessStream.ReadAsync()
被调用时,我需要以某种方式将我的字节放入 targetBuffer
并将其 Length
设置为正确的值......我似乎没有管理。
我试过这个:
var stream = targetBuffer.AsStream();
while(count > 0) {
/* doing something to get next chunk of data decrypted */
// byte[] chunk is the array used to hold decrypted data
// int chunkLength is the length of data (<= chunk.Length)
count -= chunkLength;
await stream.WriteAsync(chunk, 0, chunkLength);
}
return targetBuffer;
并且 targetBuffer.Length
保持为零,但如果我尝试打印其内容,数据就在那里!
Debug.WriteLine(targetBuffer.GetByte(0..N));
我现在有一个简单的实现,它使用内存流(除了字节数组缓冲区),在那里收集数据并从中读回 targetBuffer
。这有效,但看起来很糟糕。托管流写入 byte[]
,WinRT 流写入 IBuffer
,我只是找不到解决方法,以免浪费内存和性能。
如果有任何想法,我将不胜感激。
这是现在的样子。我最终使用字节数组作为解密缓冲区,使用可调整大小的内存流作为代理。
public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer targetBuffer, uint count, InputStreamOptions options)
{
return AsyncInfo.Run<IBuffer, uint>(async (token, progress) => {
Transport.Seek(0); // Transport is InMemoryRandomAccessStream
var remaining = count;
while(remaining > 0) {
/*
ReadAsync() overload reads & decrypts data,
result length is <= remaining bytes,
deals with block cipher alignment and the like
*/
IBuffer chunk = await ReadAsync(remaining);
await Transport.WriteAsync(chunk);
remaining -= chunk.Length;
}
Transport.Seek(0);
// copy resulting bytes to target buffer
await Transport.ReadAsync(targetBuffer, count, InputStreamOptions.None);
return targetBuffer;
});
}
更新: 我已经用 7.9Mb 的加密图像测试了上面的解决方案。我把它喂给了 Image
这样的实例:
var image = new BitmapImage();
await image.SetSourceAsync(myCustomStream);
Img.Source = image; // Img is <Image> in xaml
一切正常,直到执行达到 await Transport.ReadAsync(targetBuffer, count, InputStreamOptions.None);
:内存消耗猛增(从大约 33mb 到 300+mb),这实际上导致 phone 模拟器崩溃(桌面版显示图像正常,但内存不足)消耗相同)。到底是怎么回事?!
2017 年 3 月解决
首先,我不知何故没有意识到我可以在将数据写入缓冲区后直接设置 Length
。其次,如果你在我的案例中做错了什么(自定义 IRandomAccessStream
实现是 XAML Image
元素的来源),应用程序崩溃时不会留下任何日志也不会显示任何错误,所以真的很难弄清楚哪里出了问题。
这就是代码现在的样子:
public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer targetBuffer, uint count, InputStreamOptions options)
{
return AsyncInfo.Run<IBuffer, uint>(async (token, progress) => {
var output = targetBuffer.AsStream();
while (count > 0) {
//
// do all the decryption stuff and get decrypted data
// to a reusable buffer byte array
//
int bytes = Math.Min((int) count, BufferLength - BufferPosition);
output.Write(decrypted, bufferPosition, bytes);
targetBuffer.Length += (uint)bytes;
BufferPosition += bytes;
progress.Report((uint)bytes);
count -= (uint)bytes;
}
}
return targetBuffer;
});
使用 System.Runtime.InteropServices.WindowsRuntime;
(your byte array).CopyTo(targetBuffer);
IBuffer 中的长度 属性 有一个 setter
以下代码完全有效
targetBuffer.Length = (your integer here)
您有更多 CopyTo 变体可供选择。看看这个:
public static void CopyTo(this byte[] source, int sourceIndex, IBuffer destination, uint destinationIndex, int count);