C# 访问内存的底层数组<;T>;
在我的应用程序中,我需要迭代文件的内容,以对文件中固定大小的块进行哈希。最终目标是实现Amazon Glacier的树散列算法,我几乎一字不差地从他们的文档中复制了代码 但是,当我通过SonarQube运行以下代码时,就会出现问题:C# 访问内存的底层数组<;T>;,c#,arrays,sonarqube,buffer,filestream,C#,Arrays,Sonarqube,Buffer,Filestream,在我的应用程序中,我需要迭代文件的内容,以对文件中固定大小的块进行哈希。最终目标是实现Amazon Glacier的树散列算法,我几乎一字不差地从他们的文档中复制了代码 但是,当我通过SonarQube运行以下代码时,就会出现问题: byte[]buff=新字节[Mio]; int字节读取; 而((bytesRead=await inputStream.ReadAsync(buff,0,Mio))>0){ //处理读取的字节 } 我的while循环线路出现Roslyn问题。问题是“将'Read
byte[]buff=新字节[Mio];
int字节读取;
而((bytesRead=await inputStream.ReadAsync(buff,0,Mio))>0){
//处理读取的字节
}
我的while
循环线路出现Roslyn问题。问题是“将'ReadAsync'方法调用更改为使用'Stream.ReadAsync(内存,CancellationToken)'重载”。根据描述,这是因为使用内存类的方法比使用基本数组的方法更有效
当类可以从端到端使用时,这可能是正确的。问题是,我需要将数据提供给HashAlgorithm
的ComputeHash
方法,而它们没有任何接受内存的覆盖。这意味着我必须使用内存
的ToArray
方法来制作数据的副本。对我来说,这听起来不是很有效
我知道可以通过将现有数组传递给构造函数来创建内存实例,如下所示:
byte[]buff=新字节[Mio];
内存=新内存(buff);
int字节读取;
而((bytesRead=await inputStream.ReadAsync(内存))>0){
//使用'buff'访问字节
}
但是,文档不清楚传递给构造函数的数组是否实际用作内存
实例的底层存储
因此,我的问题如下:
- 如何将数据从
内存
直接馈送到哈希算法
实例?我指的是从HashAlgorithm
派生的类的任何实例,而不是具体的SHA256算法。我的实现不限于SHA256,这与Glacier实现不同
- 存储在
内存实例中的数据是否也可以在用于创建它的数组中访问
- 是否有其他方法可以访问存储在
内存中的数据作为数组而不复制实例
- 否则,如何在SonarQube中消除外部问题(本例中为Roslyn警告)?我没有下拉菜单来改变它的状态,就像正常的声纳问题一样
编辑添加有关代码工作方式的其他信息:
这是第一部分,从文件中计算1Mio块的第一个散列
这些是上面的while
循环的内容:
//类的构造函数
//该类实现IDisposable以正确处理算法字段
//构造函数是这样调用的
//`using TreeHash TreeHash=new TreeHash(System.Security.Cryptography.SHA512.Create())`
公共树hash(hashalgon算法){
该算法=algo;
}
//块散列生成循环
//树哈希算法的第一部分
字节[][]chunkHashes=新字节[numChunks][];
字节[]buff=新字节[Mio];
int字节读取;
int-idx=0;
而((bytesRead=await inputStream.ReadAsync(buff,0,Mio))>0){
ChunkHash[idx++]=this.ComputeHash(buff,字节读取);
}
//哈希算法的快速包装器
//也用于树哈希计算的第二部分
私有字节[]计算哈希(字节[]数据,整数计数)=>this.Algorithm.ComputeHash(数据,0,计数);
默认情况下,我使用散列算法的非固定版本,但我可能会切换到托管版本。如果需要,该方法可以变为非异步
以下方法应该有效。它利用MemoryPool
获取一个IMemoryOwner
,我们可以用它来检索我们的暂存缓冲区。我们需要一个内存
来传递给ReadAsync
调用,因此我们传递IMemoryOwner
的属性
然后,我们重新构造代码,以使用将ReadOnlySpan
作为源,将Span
作为目标的方法。我们会分配一个新的数组(而不是使用ArrayPool
),因为您正在保存/存储数组
byte[][]chunkHashes=新字节[numChunks][];
使用var memory=MemoryPool.Shared.Rent(Mio);
int字节读取;
int-idx=0;
while((bytesRead=await inputStream.ReadAsync(memory.memory,CancellationToken.None))>0)
{
var tempBuff=新字节[(int)Math.天花(this.Algorithm.HashSize/8.0)];
if(this.Algorithm.TryComputeHash(memory.memory.Span[…bytesRead]/*1*/,tempBuff,out var hashwrite))
{
ChunkHash[idx++]=HashWrited==tempBuff.Length?tempBuff:tempBuff[…hashWrited]/*2*/;
}
其他的
抛出新异常(“缓冲区不够大”);
}
对于源代码,我们传递Memory
缓冲区的属性,该属性再次从IMemoryOwner.Memory
属性检索。我们根据读取的字节数将其切片到适当的长度。作为目的地传递的Span
必须至少是算法属性的大小,即散列所需的位(而不是字节)数。由于一个实现可能(尽管我认为不太可能)使用的大小不是8的倍数,因此,如果需要,我们可以将部门的大小限制为四舍五入。我们不需要调用AsSpan
,因为有一个来自t[]
的隐式转换
我相信*最终写入的字节数将始终与HashSize
的长度相同。如果是,我们只需使用原始数组。否则,我们需要根据写入的哈希字节数将其切片到正确的长度
如果缓冲区不是l