-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpersistent_table.c
More file actions
executable file
·370 lines (305 loc) · 9.65 KB
/
persistent_table.c
File metadata and controls
executable file
·370 lines (305 loc) · 9.65 KB
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
/*
* File: persistent_table.c
*
* Implementação de funções que trabalham sobre uma tabela e um persistence
* manager.
*
* Author: sd001 > Bruno Neves, n.º 31614
* > Vasco Orey, n.º 32550
*/
#include "base64.h"
#include "persistent_table.h"
#include "persistent_table-private.h"
#include "message.h"
#include "message-private.h"
#include "utils.h"
#include "list.h"
#include "list-private.h"
/*
* Abre o acesso a uma tabela persistente, passando como parâmetro a tabela
* a ser mantida em memória e o gestor de persistência a ser usado para manter
* logs e checkpoints. Retorna a tabela persistente criada ou NULL em caso de
* erro.
*/
struct ptable_t *ptable_open(struct table_t *table, struct pmanager_t *pmanager) {
//varifica a validade dos parâmetros
if(table == NULL && pmanager == NULL) {
ERROR("persistent_table: NULL table or NULL pmanager");
return NULL;
}
//aloca memória e cria a nova tabela persistente
struct ptable_t *ptable;
if((ptable = (struct ptable_t *) malloc(sizeof(struct ptable_t))) == NULL) {
ERROR("persistent_table: malloc ptable");
return NULL;
}
ptable->table = table;
ptable->pmanager = pmanager;
//verifica se existem dados no log e caso existam, actualiza a tabela
if(pmanager_have_data(ptable->pmanager)) {
//preenche a tabela com os dados
if(pmanager_fill_state(ptable->pmanager, ptable->table) != 0) {
ERROR("persistent_table: pmanager_fill_state");
}
//cria um ficheiro temporário com o estado da tabela
if(pmanager_store_table(ptable->pmanager, ptable->table) < 0) {
ERROR("persistent_table: pmanager_store_table");
return NULL;
}
//cria um ficheiro temporário com o estado da tabela
if(pmanager_rotate_log(ptable->pmanager) != 0) {
ERROR("persistent_table: pmanager_rotate_log");
return NULL;
}
}
//em caso de sucesso
return ptable;
}
/*
* Fecha o acesso a esta tabela persistente. Todas as operações em table
* devem falhar apos um close.
*/
void ptable_close(struct ptable_t *table) {
if(table) {
//destroi a tabela
table_destroy(table->table);
//destroi o gestor de persistência
pmanager_destroy(table->pmanager);
free(table);
}
}
/*
* Liberta toda memória e apaga todos os ficheiros utilizados pela tabela.
*/
void ptable_destroy(struct ptable_t *table) {
if(table) {
//destroi a tabela
table_destroy(table->table);
//destroi o gestor de persistência e limpa os ficheiros
pmanager_destroy_clear(table->pmanager);
free(table);
}
}
/*
* Função para adicionar um elemento na tabela.
* A função vai *COPIAR* a key (string) e os dados num espaco de memória
* alocado por malloc().
* Se a key ja existe, vai substituir essa entrada pelos novos dados.
* Devolve 0 (ok) ou -1 (out of memory).
*/
int ptable_put(struct ptable_t *table, char *key, struct data_t *data) {
struct timeval start, end;
long secDiff, usecDiff;
//verifica a validade dos parâmetros
if(table == NULL || key == NULL || data == NULL) {
ERROR("persistent_table: NULL table or key or data");
return -1;
}
//insere a entrada na tabela
if(table_put(table->table, key, data) != 0) {
ERROR("persistent_table: table_put auxKey, auxData");
return -1;
}
//prepara a mensagem
char *encodedData;
int encodedSize;
if(data->data) {
//printf("Data tem dados...\n");
if((encodedSize = base64_encode_alloc(data->data, data->datasize, &encodedData)) < 0) {
ERROR("persistent_table: base64_encode_alloc");
return -1;
}
} else if((encodedSize = base64_encode_alloc("0", 2, &encodedData)) < 0) {
ERROR("persistent_table: base64_encode_alloc");
return -1;
}
size_t encodedTsSize;
char *ts = NULL;
encode_timestamp(data->timestamp, &encodedTsSize, &ts);
if(encodedTsSize <= 0) {
ERROR("encode_timestamp");
return -1;
}
ts[encodedTsSize] = '\0';
encodedData[encodedSize] = '\0';
char *msg = NULL; //formato: "put TS-BASE64 key DATA-BASE64"
if((msg = (char *) malloc(sizeof(char) * (7 + strlen(key) + encodedSize + encodedTsSize))) == NULL) {
ERROR("persistent_table: malloc msg");
return -1;
}
sprintf(msg, "put %s %s %s", ts, key, encodedData);
//printf("Log: %s -> %d\n", msg, (int)strlen(msg));
if(PRINT_LATENCIES) {
gettimeofday(&start, NULL);
}
//regista a operação no log
if((pmanager_log(table->pmanager, msg)) < 0) {
//ERROR("persistent_table: pmanager_log");
//cria um ficheiro temporário com o estado da tabela
if(pmanager_store_table(table->pmanager, table->table) < 0) {
ERROR("persistent_table: pmanager_store_table");
return -1;
}
//cria um ficheiro temporário com o estado da tabela
if(pmanager_rotate_log(table->pmanager) != 0) {
ERROR("persistent_table: pmanager_rotate_log");
return -1;
}
if((pmanager_log(table->pmanager, msg)) < 0) {
free(msg);
return -1;
}
}
if(PRINT_LATENCIES) {
gettimeofday(&end, NULL);
secDiff = end.tv_sec - start.tv_sec;
if(secDiff > 0) {
// Como passou um segundo entretanto n podemos fazer somente end - start
// A diff do start = 1000000 - start.tv_usec
// A diff do end = end.tv_usec
// total diff = end.tv_usec + (1000000 - start.tv_usec)
usecDiff = end.tv_usec + (1000000 - start.tv_usec);
} else {
usecDiff = end.tv_usec - start.tv_usec;
}
//printf("(╯°□°)╯︵ ┻━┻\n");
printf("*** Time elapsed: %ld us.\n", usecDiff);
}
//em caso de sucesso
free(msg);
free(ts);
free(encodedData);
return 0;
}
/*
* Função para obter um elemento da tabela.
* O argumento key indica a key da entrada da tabela. A função
* aloca memória para armazenar uma *CÓPIA* dos dados da tabela e
* retorna este endereço. O programa a usar essa função deve
* desalocar (utilizando free()) esta memória.
* Em caso de erro, devolve NULL.
*/
struct data_t *ptable_get(struct ptable_t *table, char *key) {
//verifica a validade dos parâmetros
if(table == NULL || key == NULL) {
ERROR("persistent_table: NULL table or key");
return NULL;
}
return table_get(table->table, key);
}
/*
* Função para remover um elemento da tabela. Vai desalocar
* toda a memória alocada na respectiva operação ptabel_put().
* Devolve: 0 (ok), -1 (key not found).
*/
int ptable_del(struct ptable_t *table, char *key) {
//verifica a validade dos parâmetros
if(table == NULL || key == NULL) {
ERROR("persistent_table: NULL table or key");
return -1;
}
//insere a entrada na tabela
if(table_del(table->table, key) != 0) {
ERROR("persistent_table: table_del");
return -1;
}
//prepara a mensagem
char *msg; //formato: "del key"
if((msg = (char *) malloc(sizeof(char) * (5 + strlen(key)))) == NULL) {
ERROR("persistent_table: malloc msg");
return -1;
}
sprintf(msg, "del %s", key);
//regista a operação no log
if((pmanager_log(table->pmanager, msg)) < 0) {
//cria um ficheiro temporário com o estado da tabela
if(pmanager_store_table(table->pmanager, table->table) < 0) {
ERROR("persistent_table: pmanager_store_table");
return -1;
}
//cria um ficheiro temporário com o estado da tabela
if(pmanager_rotate_log(table->pmanager) != 0) {
ERROR("persistent_table: pmanager_rotate_log");
return -1;
}
if((pmanager_log(table->pmanager, msg)) < 0) {
free(msg);
return -1;
}
}
//em caso de sucesso
free(msg);
return 0;
}
/*
* Devolve o número de elementos da tabela.
*/
int ptable_size(struct ptable_t *table) {
if(table) {
return table_size(table->table);
}
//em caso de erro
return -1;
}
/*
* Devolve um array de char * com a cópia de todas as keys da tabela,
* e um último elemento a NULL.
*/
char **ptable_get_keys(struct ptable_t *table) {
if(table) {
return table_get_keys(table->table);
}
//em caso de erro
return NULL;
}
/*
* Liberta a memória alocada por ptable_get_keys().
*/
void ptable_free_keys(char **keys) {
if(keys) {
table_free_keys(keys);
}
}
/*
* Função para obter o timestamp do valor associado a essa chave.
* Em caso de erro devolve -1. Em caso de chave nao encontrada devolve 0.
*/
long ptable_get_ts(struct ptable_t *ptable, char *key) {
// Verifica os parâmetros
if(ptable == NULL || key == NULL) {
ERROR("NULL ptable or key");
return -1;
}
// Procura a entrada na ptabela
long ts;
if((ts = table_get_ts(ptable->table, key)) < 0) {
ERROR("table_get_ts");
return ts;
}
//printf("Timestamp encontrado: %ld\n", ts);
// Em caso de sucesso
return ts;
}
int ptable_collect_garbage(struct ptable_t *ptable) {
if(!ptable) {
return -1;
}
struct table_t *table = ptable->table;
struct node_t *node = NULL, *nextNode;
int currentList = 0, element = 0, max = 0;
for(currentList = 0; currentList < table->hashSize; currentList ++) {
node = table->table[currentList]->head;
max = table->table[currentList]->elements;
element = 0;
while(node && element < max) {
nextNode = node->next;
if(memcmp(node->entry->value->data, "0", 1) == 0 && node->entry->value->datasize == 1) {
printf("Deleting key: %s\n", node->entry->key);
table_del(table, node->entry->key);
}
element ++;
node = nextNode;
}
}
return 0;
}