1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
// Copyright 2009-2022 Josh Close
// This file is a part of CsvHelper and is dual licensed under MS-PL and Apache 2.0.
// See LICENSE.txt for details or visit http://www.opensource.org/licenses/ms-pl.html for MS-PL and http://opensource.org/licenses/Apache-2.0 for Apache 2.0.
// https://github.com/JoshClose/CsvHelper
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
// https://blog.markvincze.com/back-to-basics-dictionary-part-2-net-implementation/
namespace CsvHelper
{
/// <summary>
/// Caches fields.
/// Based on C#'s <see cref="Dictionary{TKey, TValue}"/>.
/// </summary>
internal class FieldCache
{
private readonly int maxFieldSize;
private int size;
private int[] buckets;
private Entry[] entries;
private int count;
public FieldCache(int initialSize = 128, int maxFieldSize = 128)
{
this.maxFieldSize = maxFieldSize;
size = initialSize;
buckets = new int[size];
entries = new Entry[size];
}
public string GetField(char[] buffer, int start, int length)
{
if (length == 0)
{
return string.Empty;
}
if (length > maxFieldSize)
{
return new string(buffer, start, length);
}
var hashCode = GetHashCode(buffer, start, length);
ref var bucket = ref GetBucket(hashCode);
int i = bucket - 1;
while ((uint)i < (uint)entries.Length)
{
ref var entry = ref entries[i];
if (entry.HashCode == hashCode && entry.Value.AsSpan().SequenceEqual(new Span<char>(buffer, start, length)))
{
return entry.Value;
}
i = entry.Next;
}
if (count == entries.Length)
{
Resize();
bucket = ref GetBucket(hashCode);
}
ref var reference = ref entries[count];
reference.HashCode = hashCode;
reference.Next = bucket - 1;
reference.Value = new string(buffer, start, length);
bucket = count + 1;
count++;
return reference.Value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private uint GetHashCode(char[] buffer, int start, int length)
{
unchecked
{
uint hash = 17;
for (var i = start; i < start + length; i++)
{
hash = hash * 31 + buffer[i];
}
return hash;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ref int GetBucket(uint hashCode)
{
return ref buckets[hashCode & buckets.Length - 1];
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Resize()
{
size *= 2;
var tempEntries = new Entry[size];
Array.Copy(entries, tempEntries, count);
buckets = new int[size];
for (int i = 0; i < count; i++)
{
ref var tempEntry = ref tempEntries[i];
if (tempEntry.Next >= -1)
{
ref var bucket = ref GetBucket(tempEntry.HashCode);
tempEntry.Next = bucket - 1;
bucket = i + 1;
}
}
entries = tempEntries;
}
[DebuggerDisplay("HashCode = {HashCode}, Next = {Next}, Value = {Value}")]
private struct Entry
{
public uint HashCode;
public int Next;
public string Value;
}
}
}
|