ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/public/Qzhteln/trunk/TELTest/BasicKeyValue.cs
Revision: 2
Committed: Wed Feb 11 13:07:01 2026 UTC (8 weeks, 3 days ago) by figdor32
File size: 7999 byte(s)
Log Message:
Initial check-in

File Contents

# Content
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Linq;
5 using System.Text;
6 using System.Threading.Tasks;
7
8 namespace TELTest {
9 using System;
10 using System.Collections.Generic;
11 using System.IO;
12 using System.Linq;
13
14 internal class BasicKeyValue {
15 public BasicKeyValue(string directoryName) {
16 DirectoryName = directoryName;
17 LoadAllKeys();
18 }
19
20 public long MaximumMemoryPressure { get; set; } = 2L * 1024 * 1024 * 1024; // 2 GB
21 public long CurrentMemoryPressure => m_CurrentMemoryPressure;
22 public string DirectoryName { get; set; } = "Database";
23 public int LastTuplesOut { get; protected set; }
24 private long m_CurrentMemoryPressure = 0;
25 private readonly object _dmpLock = new object();
26
27 private readonly Dictionary<char, DateTime> m_LastAccess = new Dictionary<char, DateTime>();
28 private readonly HashSet<string> m_AllKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
29 private readonly Dictionary<char, long> m_ChunkMemoryPressure = new Dictionary<char, long>();
30 private readonly Dictionary<char, Dictionary<string, byte[]>> m_Chunks = new Dictionary<char, Dictionary<string, byte[]>>();
31
32 public bool ContainsKey(string key) => m_AllKeys.Contains(key);
33
34 public byte[] this[string key] {
35 get {
36 char q = char.ToUpper(key[0]);
37
38 if (!m_AllKeys.Contains(key))
39 return null;
40
41 if (!m_Chunks.ContainsKey(q) || !m_Chunks[q].ContainsKey(key)) {
42 LoadAndFreeIfNeeded(q);
43 }
44
45 if (m_Chunks.ContainsKey(q) && m_Chunks[q].ContainsKey(key)) {
46 m_LastAccess[q] = DateTime.Now;
47 return m_Chunks[q][key];
48 }
49
50 return null;
51 }
52 set => AddOrSet(key, value);
53 }
54
55 public void Remove(string key) {
56 char c = char.ToUpper(key[0]);
57 if (!m_AllKeys.Remove(key)) return;
58
59 if (m_Chunks.ContainsKey(c)) {
60 var oldValue = m_Chunks[c][key];
61 m_Chunks[c].Remove(key);
62 m_ChunkMemoryPressure[c] -= oldValue.Length;
63 m_CurrentMemoryPressure -= oldValue.Length;
64 } else {
65 LoadAndFreeIfNeeded(c);
66 if (m_Chunks[c].ContainsKey(key)) {
67 var loadedValue = m_Chunks[c][key];
68 if (m_Chunks[c].Remove(key)) {
69 m_ChunkMemoryPressure[c] -= loadedValue.Length;
70 m_CurrentMemoryPressure -= loadedValue.Length;
71 }
72 }
73 }
74 }
75
76 public void SavePendingChanges() => DumpAll();
77
78 protected void AddOrSet(string key, byte[] value) {
79 char q = char.ToUpper(key[0]);
80
81 PrepAddChunk(q);
82
83 if (m_Chunks[q].TryGetValue(key, out var oldValue)) {
84 m_Chunks[q][key] = value;
85 long delta = value.Length - oldValue.Length;
86 m_ChunkMemoryPressure[q] += delta;
87 m_CurrentMemoryPressure += delta;
88 } else {
89 m_AllKeys.Add(key);
90 m_Chunks[q].Add(key, value);
91 m_ChunkMemoryPressure[q] += value.Length;
92 m_CurrentMemoryPressure += value.Length;
93 }
94 }
95
96 protected void LoadAndFreeIfNeeded(char at) {
97 while (m_CurrentMemoryPressure > MaximumMemoryPressure && m_LastAccess.Any()) {
98 var oldest = m_LastAccess.OrderBy(x => x.Value).First();
99 UnloadChunk(oldest.Key);
100 }
101 LoadChunk(at);
102 }
103
104 protected void UnloadChunk(char chk) {
105 chk = char.ToUpper(chk);
106 if (!m_Chunks.ContainsKey(chk)) return;
107
108 WriteChunk(chk);
109
110 m_CurrentMemoryPressure -= m_ChunkMemoryPressure[chk];
111 m_ChunkMemoryPressure.Remove(chk);
112
113 m_Chunks.Remove(chk);
114 m_LastAccess.Remove(chk);
115 }
116
117 public void DumpAll() {
118 LastTuplesOut = 0;
119 lock (_dmpLock) {
120 if (!Directory.Exists(DirectoryName))
121 Directory.CreateDirectory(DirectoryName);
122
123 foreach (var item in m_Chunks.Keys.ToList()) {
124 WriteChunk(item);
125 }
126 }
127 }
128
129 protected void WriteChunk(char c) {
130 c = char.ToUpper(c);
131 if (!m_Chunks.ContainsKey(c)) return;
132
133 string fp = Path.Combine(DirectoryName, c + ".bin");
134
135 using (var str = File.Open(fp, FileMode.Create, FileAccess.Write))
136 using (var bw = new BinaryWriter(str, System.Text.Encoding.UTF8, false)) {
137 foreach (var kv in m_Chunks[c]) {
138 byte[] idBytes = System.Text.Encoding.UTF8.GetBytes(kv.Key);
139 bw.Write(idBytes.Length);
140 bw.Write(idBytes);
141
142 byte[] valueBytes = kv.Value ?? new byte[0];
143 bw.Write(valueBytes.Length);
144 bw.Write(valueBytes);
145 LastTuplesOut++;
146 }
147 bw.Write(0); // terminator
148 }
149 }
150
151 protected void LoadAllKeys() {
152 if (!Directory.Exists(DirectoryName)) return;
153
154 foreach (var chunk in Directory.GetFiles(DirectoryName, "*.bin")) {
155 var cn = Path.GetFileNameWithoutExtension(chunk)[0];
156 m_LastAccess[cn] = DateTime.MinValue;
157
158 using (var fs = File.OpenRead(chunk))
159 using (var br = new BinaryReader(fs, System.Text.Encoding.UTF8, true)) {
160 while (true) {
161 int idlen = br.ReadInt32();
162 if (idlen == 0) break;
163
164 string id = System.Text.Encoding.UTF8.GetString(br.ReadBytes(idlen));
165 int ctlen = br.ReadInt32();
166 fs.Seek(ctlen, SeekOrigin.Current);
167
168 m_AllKeys.Add(id);
169 }
170 }
171 }
172 }
173
174 protected void PrepAddChunk(char chk) {
175 chk = char.ToUpper(chk);
176 if (!m_Chunks.ContainsKey(chk))
177 m_Chunks[chk] = new Dictionary<string, byte[]>(StringComparer.OrdinalIgnoreCase);
178
179 if (!m_ChunkMemoryPressure.ContainsKey(chk))
180 m_ChunkMemoryPressure[chk] = 0L;
181
182 m_LastAccess[chk] = DateTime.Now;
183 }
184
185 protected bool LoadChunk(char chk) {
186 chk = char.ToUpper(chk);
187 if (!Directory.Exists(DirectoryName)) return false;
188
189 string path = Path.Combine(DirectoryName, chk + ".bin");
190 if (!File.Exists(path)) return false;
191 if (m_Chunks.ContainsKey(chk)) return true;
192
193 PrepAddChunk(chk);
194
195 using (var str = File.OpenRead(path))
196 using (var br = new BinaryReader(str, System.Text.Encoding.UTF8, true)) {
197 while (true) {
198 int idl = br.ReadInt32();
199 if (idl == 0) break;
200
201 string id = System.Text.Encoding.UTF8.GetString(br.ReadBytes(idl));
202 int ctlen = br.ReadInt32();
203 byte[] ctbytes = br.ReadBytes(ctlen);
204
205 m_Chunks[chk][id] = ctbytes;
206 m_AllKeys.Add(id);
207
208 long mp = id.Length + ctbytes.Length;
209 m_CurrentMemoryPressure += mp;
210 m_ChunkMemoryPressure[chk] += mp;
211 }
212 }
213
214 return true;
215 }
216 }
217
218 }