Skip to content

Commit bd85f86

Browse files
committed
Optimize KVStore for space
1 parent 6ddcc56 commit bd85f86

File tree

1 file changed

+129
-49
lines changed

1 file changed

+129
-49
lines changed

src/PAL/HAPPlatformKeyValueStore.cpp

+129-49
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@
2020

2121
#include <stdio.h>
2222

23-
#include <map>
2423
#include <cstdlib>
2524
#include <cstring>
2625
#include <string>
2726

28-
#include "mgos.h"
27+
#include "mgos.hpp"
2928

3029
class KVStore {
3130
public:
3231
explicit KVStore(const char* fileName);
32+
~KVStore();
3333
HAPError
3434
Get(HAPPlatformKeyValueStoreDomain domain,
3535
HAPPlatformKeyValueStoreKey key,
@@ -41,8 +41,9 @@ class KVStore {
4141
Set(HAPPlatformKeyValueStoreDomain domain,
4242
HAPPlatformKeyValueStoreKey key,
4343
const void* bytes,
44-
size_t numBytes);
45-
HAPError Remove(HAPPlatformKeyValueStoreDomain domain, HAPPlatformKeyValueStoreKey key);
44+
size_t numBytes,
45+
bool save = true);
46+
HAPError Remove(HAPPlatformKeyValueStoreDomain domain, HAPPlatformKeyValueStoreKey key, bool save = true);
4647
HAPError Enumerate(
4748
HAPPlatformKeyValueStoreRef keyValueStore,
4849
HAPPlatformKeyValueStoreDomain domain,
@@ -51,20 +52,62 @@ class KVStore {
5152
HAPError PurgeDomain(HAPPlatformKeyValueStoreDomain domain);
5253

5354
private:
55+
struct Item {
56+
Item* next;
57+
HAPPlatformKeyValueStoreDomain dom;
58+
HAPPlatformKeyValueStoreKey key;
59+
uint16_t len;
60+
uint8_t data[];
61+
62+
static Item*
63+
New(HAPPlatformKeyValueStoreDomain domain,
64+
HAPPlatformKeyValueStoreKey key,
65+
const void* data,
66+
uint8_t len) {
67+
Item* itm = reinterpret_cast<Item*>(new uint8_t[sizeof(Item) + len]);
68+
itm->next = nullptr;
69+
itm->dom = domain;
70+
itm->key = key;
71+
itm->len = len;
72+
std::memcpy(itm->data, data, len);
73+
return itm;
74+
}
75+
76+
static void Delete(Item* itm) {
77+
delete[] reinterpret_cast<uint8_t*>(itm);
78+
}
79+
};
80+
5481
static uint16_t KVSKey(HAPPlatformKeyValueStoreDomain domain, HAPPlatformKeyValueStoreKey key);
5582
void Load();
83+
void Clear();
5684
HAPError Save() const;
5785

5886
const std::string fileName_;
59-
std::map<uint16_t, std::string> kvs_;
87+
Item* items_ = nullptr;
6088
};
6189

6290
KVStore::KVStore(const char* fileName)
6391
: fileName_(fileName) {
6492
Load();
6593
}
6694

95+
KVStore::~KVStore() {
96+
Clear();
97+
}
98+
99+
void KVStore::Clear() {
100+
Item *itm = items_, *next;
101+
while (itm != nullptr) {
102+
next = itm->next;
103+
Item::Delete(itm);
104+
itm = next;
105+
}
106+
items_ = nullptr;
107+
}
108+
67109
void KVStore::Load() {
110+
Clear();
68111
void* h = NULL;
69112
size_t size = 0;
70113
char* data = cs_read_file(fileName_.c_str(), &size);
@@ -77,12 +120,8 @@ void KVStore::Load() {
77120
rename(tmpFileName.c_str(), fileName_.c_str());
78121
}
79122
}
80-
if (data == NULL) {
81-
LOG(LL_WARN, ("No KVStore data"));
82-
goto out;
83-
}
84-
85-
kvs_.clear();
123+
mgos::ScopedCPtr data_owner(data);
124+
int num_keys = 0;
86125
struct json_token key, val;
87126
while ((h = json_next_key(data, size, h, "", &key, &val)) != NULL) {
88127
char* v = NULL;
@@ -94,15 +133,14 @@ void KVStore::Load() {
94133
val.ptr--;
95134
val.len += 2;
96135
if (json_scanf(val.ptr - 1, val.len + 2, "%V", &v, &vs) == 1) {
97-
kvs_[k] = std::string(v, vs);
136+
HAPPlatformKeyValueStoreDomain dd = (HAPPlatformKeyValueStoreDomain)(k >> 8);
137+
HAPPlatformKeyValueStoreKey kk = (HAPPlatformKeyValueStoreKey) k;
138+
Set(dd, kk, v, vs, false /* save */);
139+
num_keys++;
98140
free(v);
99141
}
100142
}
101-
102-
LOG(LL_DEBUG, ("Loaded %d keys from %s", (int) kvs_.size(), fileName_.c_str()));
103-
104-
out:
105-
free(data);
143+
LOG(LL_DEBUG, ("Loaded %d keys from %s ss %d", num_keys, fileName_.c_str(), (int) sizeof(Item)));
106144
}
107145

108146
HAPError KVStore::Save() const {
@@ -116,13 +154,14 @@ HAPError KVStore::Save() const {
116154
struct json_out out;
117155
out = JSON_OUT_FILE(fp);
118156
int num;
119-
num = 0;
157+
const Item* itm;
120158
json_printf(&out, "{");
121-
for (auto it = kvs_.begin(); it != kvs_.end(); it++, num++) {
159+
for (itm = items_, num = 0; itm != nullptr; itm = itm->next, num++) {
122160
if (num != 0) {
123161
json_printf(&out, ",");
124162
}
125-
if (json_printf(&out, "\n \"%d\": %V", it->first, it->second.data(), (int) it->second.size()) < 0) {
163+
uint16_t fkey = ((((uint16_t) itm->dom) << 8) | ((uint16_t) itm->key));
164+
if (json_printf(&out, "\n \"%u\": %V", fkey, &itm->data[0], itm->len) < 0) {
126165
goto out;
127166
}
128167
}
@@ -138,8 +177,9 @@ HAPError KVStore::Save() const {
138177
err = kHAPError_None;
139178

140179
out:
141-
if (fp != NULL)
180+
if (fp != NULL) {
142181
fclose(fp);
182+
}
143183
return err;
144184
}
145185

@@ -155,14 +195,15 @@ HAPError KVStore::Get(
155195
size_t maxBytes,
156196
size_t* _Nullable numBytes,
157197
bool* found) const {
158-
auto it = kvs_.find(KVSKey(domain, key));
159-
if (it == kvs_.end()) {
160-
*found = false;
161-
*numBytes = 0;
162-
} else {
163-
*numBytes = std::min(it->second.size(), maxBytes);
164-
std::memcpy(bytes, it->second.data(), *numBytes);
198+
*found = false;
199+
for (const Item* itm = items_; itm != nullptr; itm = itm->next) {
200+
if (itm->dom != domain || itm->key != key) {
201+
continue;
202+
}
203+
*numBytes = std::min((size_t) itm->len, maxBytes);
204+
std::memcpy(bytes, &itm->data[0], *numBytes);
165205
*found = true;
206+
break;
166207
}
167208
return kHAPError_None;
168209
}
@@ -171,14 +212,47 @@ HAPError KVStore::Set(
171212
HAPPlatformKeyValueStoreDomain domain,
172213
HAPPlatformKeyValueStoreKey key,
173214
const void* bytes,
174-
size_t numBytes) {
175-
kvs_[KVSKey(domain, key)] = std::string(static_cast<const char*>(bytes), numBytes);
176-
return Save();
215+
size_t numBytes,
216+
bool save) {
217+
Item *prev = nullptr, *itm = items_, *next = nullptr;
218+
while (itm != nullptr) {
219+
if (itm->dom == domain && itm->key == key) {
220+
break;
221+
}
222+
prev = itm;
223+
itm = itm->next;
224+
}
225+
if (itm != nullptr) {
226+
next = itm->next;
227+
}
228+
if (itm != nullptr) {
229+
Item::Delete(itm);
230+
}
231+
bool changed;
232+
if (bytes != nullptr) {
233+
itm = Item::New(domain, key, bytes, numBytes);
234+
if (itm == nullptr) {
235+
return kHAPError_OutOfResources;
236+
}
237+
changed = true;
238+
} else {
239+
changed = (itm != nullptr);
240+
itm = next;
241+
next = nullptr;
242+
}
243+
if (prev != nullptr) {
244+
prev->next = itm;
245+
} else {
246+
items_ = itm;
247+
}
248+
if (next != nullptr) {
249+
itm->next = next;
250+
}
251+
return (changed && save ? Save() : kHAPError_None);
177252
}
178253

179-
HAPError KVStore::Remove(HAPPlatformKeyValueStoreDomain domain, HAPPlatformKeyValueStoreKey key) {
180-
size_t numErased = kvs_.erase(KVSKey(domain, key));
181-
return (numErased == 0 ? kHAPError_None : Save());
254+
HAPError KVStore::Remove(HAPPlatformKeyValueStoreDomain domain, HAPPlatformKeyValueStoreKey key, bool save) {
255+
return Set(domain, key, nullptr, 0, save);
182256
}
183257

184258
HAPError KVStore::Enumerate(
@@ -187,13 +261,12 @@ HAPError KVStore::Enumerate(
187261
HAPPlatformKeyValueStoreEnumerateCallback callback,
188262
void* _Nullable context) const {
189263
HAPError err = kHAPError_None;
190-
const auto from_it = kvs_.lower_bound(KVSKey(domain, 0x00));
191-
const auto to_it = kvs_.lower_bound(KVSKey(domain, 0xff));
192-
for (auto it = from_it; it != to_it; it++) {
193-
HAPPlatformKeyValueStoreDomain kd = static_cast<HAPPlatformKeyValueStoreDomain>(it->first >> 8);
194-
HAPPlatformKeyValueStoreKey kk = static_cast<HAPPlatformKeyValueStoreKey>(it->first);
264+
for (const Item* itm = items_; itm != nullptr; itm = itm->next) {
265+
if (itm->dom != domain) {
266+
continue;
267+
}
195268
bool shouldContinue = true;
196-
err = callback(context, keyValueStore, kd, kk, &shouldContinue);
269+
err = callback(context, keyValueStore, itm->dom, itm->key, &shouldContinue);
197270
if (err != kHAPError_None || !shouldContinue) {
198271
break;
199272
}
@@ -202,15 +275,22 @@ HAPError KVStore::Enumerate(
202275
}
203276

204277
HAPError KVStore::PurgeDomain(HAPPlatformKeyValueStoreDomain domain) {
205-
const auto from_it = kvs_.lower_bound(KVSKey(domain, 0x00));
206-
const auto to_it = kvs_.lower_bound(KVSKey(domain, 0xff));
207-
size_t numErased = 0;
208-
for (auto it = from_it; it != to_it;) {
209-
auto it2 = it++;
210-
kvs_.erase(it2);
211-
numErased++;
278+
bool changed = false;
279+
while (true) {
280+
Item* itm = items_;
281+
while (itm != nullptr) {
282+
if (itm->dom == domain) {
283+
break;
284+
}
285+
itm = itm->next;
286+
}
287+
if (itm == nullptr) {
288+
break;
289+
}
290+
Remove(itm->dom, itm->key, false /* save */);
291+
changed = true;
212292
}
213-
return (numErased == 0 ? kHAPError_None : Save());
293+
return (changed ? Save() : kHAPError_None);
214294
}
215295

216296
extern "C" {
@@ -265,7 +345,7 @@ void HAPPlatformKeyValueStoreCreate(
265345
void HAPPlatformKeyValueStoreRelease(HAPPlatformKeyValueStoreRef keyValueStore) {
266346
auto* kvs = static_cast<KVStore*>(keyValueStore->ctx);
267347
keyValueStore->ctx = nullptr;
268-
delete (kvs);
348+
delete kvs;
269349
}
270350

271351
} // extern "C"

0 commit comments

Comments
 (0)