-
Notifications
You must be signed in to change notification settings - Fork 25
/
Config.cpp
513 lines (394 loc) · 11.6 KB
/
Config.cpp
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
#include "stddef.h"
#include "Config.h"
#include "Device\Flash.h"
#include "Security\Crc.h"
#define CFG_DEBUG DEBUG
//#define CFG_DEBUG 0
#if CFG_DEBUG
//#define CTS TS
#else
#undef TS
#define TS(name)
#endif
const Config* Config::Current = nullptr;
/*================================ 配置块 ================================*/
// 配置块。名称、长度、头部校验,数据部分不做校验,方便外部修改
struct ConfigBlock
{
ushort Hash;
ushort Size;
char Name[8]; // 零结尾字符串
ushort GetHash() const;
bool Valid() const;
const ConfigBlock* Next() const;
const void* Data() const;
uint CopyTo(Buffer& bs) const;
bool Init(const String& name, const Buffer& bs);
bool Write(const Storage& storage, uint addr, const Buffer& bs);
bool Remove(const Storage& storage, uint addr);
};
ushort ConfigBlock::GetHash() const
{
// 计算头部 CRC。从数据CRC开始,包括大小和名称
return Crc::Hash16(Buffer((byte*)&Size, sizeof(*this) - offsetof(ConfigBlock, Size)));
}
bool ConfigBlock::Valid() const
{
return GetHash() == Hash;
}
// 获取下一块。当前块必须有效,否则返回空,下一块不在乎有效无效
const ConfigBlock* ConfigBlock::Next() const
{
if(!Valid()) return nullptr;
// 确保数据部分2字节对齐,便于Flash操作
uint s = (Size + 1) & 0xFFFE;
return (const ConfigBlock*)((byte*)Data() + s);
}
// 数据所在地址,紧跟头部后面
const void* ConfigBlock::Data() const
{
return (const void*)&this[1];
}
uint ConfigBlock::CopyTo(Buffer& bs) const
{
if(Size == 0 || Size > bs.Length()) return 0;
return bs.Copy(0, Data(), Size);
}
// 构造一个新的配置块
bool ConfigBlock::Init(const String& name, const Buffer& bs)
{
if(!name) return false;
assert(name.Length() < (int)sizeof(Name), "配置区名称太长");
if(name.Length() >= (int)sizeof(Name)) return false;
//TS("ConfigBlock::Init");
// 配置块的大小,只有第一次能够修改,以后即使废弃也不能修改,仅仅清空名称
if(bs.Length() > 0)
{
Size = bs.Length();
name.CopyTo(0, Name, -1);
}
Hash = GetHash();
return true;
}
// 更新块
bool ConfigBlock::Write(const Storage& storage, uint addr, const Buffer& bs)
{
TS("ConfigBlock::Write");
// 如果大小超标,并且下一块有效,那么这是非法操作
if(bs.Length() > Size && Next()->Valid())
{
debug_printf("ConfigBlock::Write 配置块 %s 大小 %d < %d \r\n", Name, Size, bs.Length());
return false;
}
Hash = GetHash();
// 先写入头部,然后写入数据
uint len = sizeof(ConfigBlock) - offsetof(ConfigBlock, Hash);
// 合并写入,减少擦除次数
if(Size <= 512 && len + bs.Length() <= 512)
{
byte buf[512];
Buffer ds(buf, 512);
ds.Copy(0, &Hash, len);
ds.Copy(len, bs, 0, bs.Length());
ds.SetLength(len + bs.Length());
if(!storage.Write(addr, ds)) return false;
}
else
{
if(!storage.Write(addr, Buffer(&Hash, len))) return false;
if(bs.Length() > 0)
{
uint len2 = bs.Length();
if(len2 > Size) len2 = Size;
if(!storage.Write(addr + len, bs.Sub(0, len2))) return false;
}
}
return true;
}
// 删除块。名称清空,如果下一块有效,则保留大小和数据区,避免找不到下一块区域
bool ConfigBlock::Remove(const Storage& storage, uint addr)
{
// 把整个名称区域清空
Buffer(Name, sizeof(Name)).Clear();
// 如果下一块有效,则保留大小和数据区,避免找不到下一块区域
// 如果下一块有效,且名称为空,则需要把两块连在一起
// 否则,把长度也清零,让它跟后面的区域连在一起
auto p = Next();
if(p && p->Valid())
{
// 如果下一个也是空的,连在一起
if(!Name[0]) Size += sizeof(ConfigBlock) + p->Size;
}
else
{
Size = 0;
}
Hash = GetHash();
// 写入头部
uint len = sizeof(ConfigBlock) - offsetof(ConfigBlock, Hash);
return storage.Write(addr, Buffer(&Hash, len));
}
/*================================ 配置 ================================*/
Config::Config(const Storage& st, uint addr, uint size)
: Device(st)
{
Address = addr;
Size = size;
}
// 检查签名
bool CheckSignature(const Storage& st, uint& addr, bool create)
{
const uint c_Version = 0x534F5453; // STOS
// 检查签名,如果不存在则写入
if(*(uint*)addr != c_Version)
{
if(!create) return false;
st.Write(addr, Buffer((byte*)&c_Version, sizeof(c_Version)));
}
addr += sizeof(c_Version);
return true;
}
// 循环查找配置块
const ConfigBlock* FindBlock(const Storage& st, uint addr, const String& name)
{
TS("Config::Find");
if(!name) return nullptr;
assert(name.Length() < (int)sizeof(ConfigBlock::Name), "配置区名称太长");
if(name.Length() >= (int)sizeof(ConfigBlock::Name)) return nullptr;
if(!CheckSignature(st, addr, false)) return nullptr;
// 第一个配置块
auto cfg = (const ConfigBlock*)addr;
// 遍历链表,找到同名块
while(cfg->Valid())
{
if(cfg->Name[0] && name == cfg->Name) return cfg;
cfg = cfg->Next();
}
return nullptr;
}
// 创建一个指定大小的配置块。找一个满足该大小的空闲数据块,或者在最后划分一个
const ConfigBlock* NewBlock(const Storage& st, uint addr, int size)
{
TS("Config::New");
if(!CheckSignature(st, addr, true)) return nullptr;
// 第一个配置块
auto cfg = (const ConfigBlock*)addr;
// 找一块合适大小的空闲区域
while(cfg->Valid())
{
if(cfg->Name[0] == 0 && cfg->Size == size) return cfg;
cfg = cfg->Next();
}
return cfg;
}
// 循环查找配置块
const void* Config::Find(const String& name) const
{
return FindBlock(Device, Address, name);
}
// 创建一个指定大小的配置块。找一个满足该大小的空闲数据块,或者在最后划分一个
const void* Config::New(int size) const
{
auto cfg = NewBlock(Device, Address, size);
// 实在没办法,最后划分一个新的区块。这里判断一下空间是否足够
if(Size && (uint)cfg + sizeof(ConfigBlock) + size > Address + Size)
{
debug_printf("Config::New 0x%p + %d + %d 配置区(0x%p, %d)空间不足\r\n", cfg, sizeof(ConfigBlock), size, (byte*)Address, Size);
return nullptr;
}
return cfg;
}
// 删除
bool Config::Remove(const String& name) const
{
//return Set(name, ByteArray(0));
TS("Config::Remove");
if(!name) return false;
auto cfg = FindBlock(Device, Address, name);
if(!cfg) return false;
// 只清空名称,修改哈希,不能改大小,否则无法定位下一个配置块
// 拷贝一份修改
auto header = *cfg;
return header.Remove(Device, (uint)cfg);
}
bool Config::RemoveAll() const
{
TS("Config::RemoveAll");
/*int count = 0;
uint addr = Address;
if(!CheckSignature(Device, addr, false)) return count;
// 第一个配置块
auto cfg = (const ConfigBlock*)addr;
// 遍历链表,找到同名块
while(cfg->Valid())
{
count++;
#if CFG_DEBUG
debug_printf("Config::RemoveAll %d %s \r\n", count, cfg->Name);
#endif
auto next = cfg->Next();
// 重新搞一个配置头
ConfigBlock header;
cfg->CopyTo(Buffer(&header, sizeof(header)));
header.Size = 0;
Device.Write(&cfg, Buffer(&Hash, len));
cfg = next;
}*/
#if CFG_DEBUG
debug_printf("Config::RemoveAll (0x%p, %d) \r\n", (byte*)Address, Size);
#endif
ByteArray bs((byte)0xFF, Size);
return Device.Write(Address, bs);
}
// 根据名称更新块
const void* Config::Set(const String& name, const Buffer& bs) const
{
TS("Config::Set");
if(!name) return nullptr;
assert(name.Length() < (int)sizeof(ConfigBlock::Name), "配置区名称太长");
if(name.Length() >= (int)sizeof(ConfigBlock::Name)) return nullptr;
auto cfg = FindBlock(Device, Address, name);
if(!cfg) cfg = NewBlock(Device, Address, bs.Length());
if(!cfg) return nullptr;
// 重新搞一个配置头,使用新的数据去重新初始化
ConfigBlock header;
header.Init(name, bs);
if(!header.Write(Device, (uint)cfg, bs)) return nullptr;
return cfg->Data();
}
// 获取配置数据
bool Config::Get(const String& name, Buffer& bs) const
{
TS("Config::Get");
if(!name) return false;
auto cfg = FindBlock(Device, Address, name);
if(!cfg) return false;
return cfg->CopyTo(bs) > 0;
}
const void* Config::Get(const String& name) const
{
TS("Config::GetByName");
if(!name) return nullptr;
auto cfg = FindBlock(Device, Address, name);
if(cfg && cfg->Size) return cfg->Data();
return nullptr;
}
/*// 获取配置数据,如果不存在则覆盖
bool Config::GetOrSet(const String& name, Buffer& bs) const
{
TS("Config::GetOrSet");
if(name == nullptr) return false;
//assert(name, "配置块名称不能为空");
// 输入数据已存在,直接返回
if(Get(name, bs)) return true;
// 否则,用这块数据去覆盖吧
Set(name, bs);
return false;
}*/
// Flash最后一块作为配置区
const Config& Config::CreateFlash()
{
// 最后一块作为配置区
static Flash flash;
static Config cfg(flash, flash.Start + flash.Size - flash.Block, flash.Block);
return cfg;
}
// RAM最后一小段作为热启动配置区
const Config& Config::CreateRAM()
{
// 最后一块作为配置区
static CharStorage cs;
static Config cfg(cs, Sys.StackTop(), 256);
/*// 从堆申请一块内存
if (cfg.Size == 0) {
cfg.Address = (uint)new byte[256];
cfg.Size = 256;
}*/
return cfg;
}
/******************************** ConfigBase ********************************/
ConfigBase::ConfigBase()
: Cfg(*Config::Current)
{
assert(&Cfg, "Cfg");
New = true;
_Name = nullptr;
}
uint ConfigBase::Size() const
{
assert(_End && _Start, "_Start & _End == nullptr");
return (uint)_End - (uint)_Start;
}
Buffer ConfigBase::ToArray()
{
return Buffer(_Start, Size());
}
const Buffer ConfigBase::ToArray() const
{
return Buffer(_Start, Size());
}
void ConfigBase::Init()
{
Buffer(_Start, Size()).Clear();
}
void ConfigBase::Load()
{
// 尝试加载配置区设置
auto bs = ToArray();
//New = !Cfg.GetOrSet(_Name, bs);
New = !Cfg.Get(_Name, bs);
if(New)
debug_printf("%s::Load 首次运行,创建配置区!\r\n", _Name);
else
debug_printf("%s::Load 从配置区加载配置 %d 字节\r\n", _Name, bs.Length());
}
void ConfigBase::Save() const
{
//auto bs = ToArray();
Buffer bs(_Start, Size());
debug_printf("%s::Save %d 字节 ", _Name, bs.Length());
auto pt = Cfg.Set(_Name, bs);
if(pt)
debug_printf("成功 0x%p \r\n", pt);
else
debug_printf("失败\r\n");
}
void ConfigBase::Clear()
{
debug_printf("%s::Clear ", _Name);
bool rs = Cfg.Remove(_Name);
if(rs)
debug_printf("成功\r\n");
else
debug_printf("失败\r\n");
}
void ConfigBase::Show() const
{
}
void ConfigBase::Write(Stream& ms) const
{
ms.Write(ToArray());
}
void ConfigBase::Read(Stream& ms)
{
auto bs = ToArray();
ms.Read(bs);
}
/******************************** HotConfig ********************************/
void* HotConfig::Next() const
{
return (void*)&this[1];
}
HotConfig& HotConfig::Current()
{
static const Config& cfg = Config::CreateRAM();
// 查找配置数据,如果不存在,则清空
auto dat = cfg.Get("Hot");
if(!dat)
{
ByteArray bs(sizeof(HotConfig));
bs.Set(0, 0, bs.Length());
dat = cfg.Set("Hot", bs);
}
return *(HotConfig*)dat;
}