Contiki-NG
Loading...
Searching...
No Matches
index-maxheap.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2010, Swedish Institute of Computer Science
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the Institute nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30/**
31 * \file
32 * MaxHeap - A binary maximum heap index for flash memory.
33 *
34 * The idea behind the MaxHeap index is to write entries sequentially
35 * into small buckets, which are indexed in a binary maximum heap.
36 * Although sequential writes make the entries unsorted within a
37 * bucket, the time to load and scan a single bucket is small. The
38 * sequential write is important for flash memories, which are
39 * unable to handle multiple rewrites of the same page without doing
40 * an expensive erase operation between the rewrites.
41 *
42 * Each bucket specifies a range (a,b) of values that it accepts.
43 * Once a bucket fills up, two buckets are created with the ranges
44 * (a,mean) and (mean+1, b), respectively. The entries from the
45 * original bucket are then copied into the appropriate new bucket
46 * before the old bucket gets deleted.
47 * \author
48 * Nicolas Tsiftes <nvt@sics.se>
49 */
50
51#include <limits.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
55
56#include "cfs/cfs.h"
57#include "cfs/cfs-coffee.h"
58#include "lib/crc16.h"
59#include "lib/memb.h"
60
61#include "db-options.h"
62#include "index.h"
63#include "result.h"
64#include "storage.h"
65
66#define DEBUG DEBUG_NONE
67#include "net/ipv6/uip-debug.h"
68
69#define BRANCH_FACTOR 2
70#define BUCKET_SIZE 128
71#define NODE_LIMIT 511
72#define NODE_DEPTH 9
73
74#if (1 << NODE_DEPTH) != (NODE_LIMIT + 1)
75#error "NODE_DEPTH is set incorrectly."
76#endif
77
78#define EMPTY_NODE(node) ((node)->min == 0 && (node)->max == 0)
79#define EMPTY_PAIR(pair) ((pair)->key == 0 && (pair)->value == 0)
80
81typedef uint16_t maxheap_key_t;
82typedef uint16_t maxheap_value_t;
83
84#define KEY_MIN 0
85#define KEY_MAX 65535
86
87struct heap_node {
88 maxheap_key_t min;
89 maxheap_key_t max;
90};
91typedef struct heap_node heap_node_t;
92
93struct key_value_pair {
94 maxheap_key_t key;
95 maxheap_value_t value;
96};
97
98struct bucket {
99 struct key_value_pair pairs[BUCKET_SIZE];
100};
101typedef struct bucket bucket_t;
102
103struct heap {
104 db_storage_id_t heap_storage;
105 db_storage_id_t bucket_storage;
106 /* Remember where the next free slot for each bucket is located. */
107 uint8_t next_free_slot[NODE_LIMIT];
108};
109typedef struct heap heap_t;
110
111struct bucket_cache {
112 heap_t *heap;
113 uint16_t bucket_id;
114 bucket_t bucket;
115};
116
117/* Keep a cache of buckets read from storage. */
118static struct bucket_cache bucket_cache[DB_HEAP_CACHE_LIMIT];
119MEMB(heaps, heap_t, DB_HEAP_INDEX_LIMIT);
120
121static struct bucket_cache *get_cache(heap_t *, int);
122static struct bucket_cache *get_cache_free(void);
123static void invalidate_cache(void);
124static maxheap_key_t transform_key(maxheap_key_t);
125static int heap_read(heap_t *, int, heap_node_t *);
126static int heap_write(heap_t *, int, heap_node_t *);
127static int heap_insert(heap_t *, maxheap_key_t, maxheap_key_t);
128static int heap_find(heap_t *, maxheap_key_t key, int *iterator);
129#if HEAP_DEBUG
130static void heap_print(heap_t *);
131#endif
132static int bucket_read(heap_t *, int, bucket_t *);
133static struct bucket_cache *bucket_load(heap_t *, int);
134static int bucket_append(heap_t *, int, struct key_value_pair *);
135static int bucket_split(heap_t *, int);
136
137static db_result_t create(index_t *);
138static db_result_t destroy(index_t *);
139static db_result_t load(index_t *);
140static db_result_t release(index_t *);
141static db_result_t insert(index_t *, attribute_value_t *, tuple_id_t);
142static db_result_t delete(index_t *, attribute_value_t *);
143static tuple_id_t get_next(index_iterator_t *);
144
145index_api_t index_maxheap = {
146 INDEX_MAXHEAP,
147 INDEX_API_EXTERNAL,
148 create,
149 destroy,
150 load,
151 release,
152 insert,
153 delete,
154 get_next
155};
156
157static struct bucket_cache *
158get_cache(heap_t *heap, int bucket_id)
159{
160 int i;
161
162 for(i = 0; i < DB_HEAP_CACHE_LIMIT; i++) {
163 if(bucket_cache[i].heap == heap && bucket_cache[i].bucket_id == bucket_id) {
164 return &bucket_cache[i];
165 }
166 }
167 return NULL;
168}
169
170static struct bucket_cache *
171get_cache_free(void)
172{
173 int i;
174
175 for(i = 0; i < DB_HEAP_CACHE_LIMIT; i++) {
176 if(bucket_cache[i].heap == NULL) {
177 return &bucket_cache[i];
178 }
179 }
180 return NULL;
181}
182
183static void
184invalidate_cache(void)
185{
186 int i;
187
188 for(i = 0; i < DB_HEAP_CACHE_LIMIT; i++) {
189 if(bucket_cache[i].heap != NULL) {
190 bucket_cache[i].heap = NULL;
191 break;
192 }
193 }
194}
195
196static maxheap_key_t
197transform_key(maxheap_key_t key)
198{
199 return crc16_data((const uint8_t *)&key, sizeof(key), 0);
200}
201
202static int
203heap_read(heap_t *heap, int bucket_id, heap_node_t *node)
204{
205 if(DB_ERROR(storage_read(heap->heap_storage, node,
206 DB_MAX_FILENAME_LENGTH + (unsigned long)bucket_id * sizeof(*node), sizeof(*node)))) {
207 return 0;
208 }
209
210 return 1;
211}
212
213static int
214heap_write(heap_t *heap, int bucket_id, heap_node_t *node)
215{
216 if(DB_ERROR(storage_write(heap->heap_storage, node,
217 DB_MAX_FILENAME_LENGTH + (unsigned long)bucket_id * sizeof(*node), sizeof(*node)))) {
218 return 0;
219 }
220
221 return 1;
222}
223
224static int
225heap_insert(heap_t *heap, maxheap_key_t min, maxheap_key_t max)
226{
227 int i;
228 heap_node_t node;
229
230 PRINTF("DB: Insert node (%ld,%ld) into the heap\n", (long)min, (long)max);
231
232 if(min > max) {
233 return -1;
234 }
235
236 for(i = 0; i < NODE_LIMIT;) {
237 if(heap_read(heap, i, &node) == 0) {
238 PRINTF("DB: Failed to read heap node %d\n", i);
239 return -1;
240 }
241
242 if(EMPTY_NODE(&node)) {
243 node.min = min;
244 node.max = max;
245 if(heap_write(heap, i, &node) == 0) {
246 PRINTF("DB: Failed to write heap node %d\n", i);
247 return -1;
248 }
249 return i;
250 } else if(node.min <= min && max <= node.max) {
251 i = BRANCH_FACTOR * i + 1;
252 } else {
253 i++;
254 }
255 }
256
257 PRINTF("DB: No more nodes available\n");
258 return -1;
259}
260
261static int
262heap_find(heap_t *heap, maxheap_key_t key, int *iterator)
263{
264 maxheap_key_t hashed_key;
265 int i;
266 int first_child;
267 static heap_node_t node;
268
269 hashed_key = transform_key(key);
270
271 for(i = *iterator; i < NODE_LIMIT;) {
272 if(heap_read(heap, i, &node) == 0) {
273 break;
274 }
275 if(EMPTY_NODE(&node)) {
276 break;
277 } else if(node.min <= hashed_key && hashed_key <= node.max) {
278 first_child = BRANCH_FACTOR * i + 1;
279
280 if(first_child >= NODE_LIMIT) {
281 break;
282 }
283 *iterator = first_child;
284 return i;
285 } else {
286 i++;
287 }
288 }
289
290 return -1;
291}
292
293#if HEAP_DEBUG
294static void
295heap_print(heap_t *heap)
296{
297 int level_count;
298 int branch_count;
299 int branch_amount;
300 int i, j;
301 heap_node_t node;
302
303 level_count = 0;
304 branch_count = 0;
305 branch_amount = BRANCH_FACTOR;
306
307 for(i = 0;; i++) {
308 if(heap_read(heap, i, &node) == 0 || EMPTY_NODE(&node)) {
309 break;
310 }
311
312 for(j = 0; j < level_count; j++) {
313 PRINTF("\t");
314 }
315 PRINTF("(%ld,%ld)\n", (long)node.min, (long)node.max);
316 if(level_count == 0) {
317 level_count++;
318 } else if(branch_count + 1 == branch_amount) {
319 level_count++;
320 branch_count = 0;
321 branch_amount = branch_amount * BRANCH_FACTOR;
322 } else {
323 branch_count++;
324 }
325 }
326}
327#endif /* HEAP_DEBUG */
328
329static int
330bucket_read(heap_t *heap, int bucket_id, bucket_t *bucket)
331{
332 size_t size;
333
334 if(heap->next_free_slot[bucket_id] == 0) {
335 size = BUCKET_SIZE;
336 } else {
337 size = heap->next_free_slot[bucket_id];
338 }
339
340 size *= sizeof(struct key_value_pair);
341
342 if(DB_ERROR(storage_read(heap->bucket_storage, bucket,
343 (unsigned long)bucket_id * sizeof(*bucket), size))) {
344 return 0;
345 }
346
347 return 1;
348}
349
350static struct bucket_cache *
351bucket_load(heap_t *heap, int bucket_id)
352{
353 int i;
354 struct bucket_cache *cache;
355
356 cache = get_cache(heap, bucket_id);
357 if(cache != NULL) {
358 return cache;
359 }
360
361 cache = get_cache_free();
362 if(cache == NULL) {
363 invalidate_cache();
364 cache = get_cache_free();
365 if(cache == NULL) {
366 return NULL;
367 }
368 }
369
370 if(bucket_read(heap, bucket_id, &cache->bucket) == 0) {
371 return NULL;
372 }
373
374 cache->heap = heap;
375 cache->bucket_id = bucket_id;
376
377 if(heap->next_free_slot[bucket_id] == 0) {
378 for(i = 0; i < BUCKET_SIZE; i++) {
379 if(EMPTY_PAIR(&cache->bucket.pairs[i])) {
380 break;
381 }
382 }
383
384 heap->next_free_slot[bucket_id] = i;
385 }
386
387 PRINTF("DB: Loaded bucket %d, the next free slot is %u\n", bucket_id,
388 (unsigned)heap->next_free_slot[bucket_id]);
389
390 return cache;
391}
392
393static int
394bucket_append(heap_t *heap, int bucket_id, struct key_value_pair *pair)
395{
396 unsigned long offset;
397
398 if(heap->next_free_slot[bucket_id] >= BUCKET_SIZE) {
399 PRINTF("DB: Invalid write attempt to the full bucket %d\n", bucket_id);
400 return 0;
401 }
402
403 offset = (unsigned long)bucket_id * sizeof(bucket_t);
404 offset += heap->next_free_slot[bucket_id] * sizeof(struct key_value_pair);
405
406 if(DB_ERROR(storage_write(heap->bucket_storage, pair, offset, sizeof(*pair)))) {
407 return 0;
408 }
409
410 heap->next_free_slot[bucket_id]++;
411
412 return 1;
413}
414
415static int
416bucket_split(heap_t *heap, int bucket_id)
417{
418 heap_node_t node;
419 maxheap_key_t mean;
420 int small_bucket_index;
421 int large_bucket_index;
422
423 if(heap_read(heap, bucket_id, &node) == 0) {
424 return 0;
425 }
426
427 mean = node.min + ((node.max - node.min) / 2);
428
429 PRINTF("DB: Split bucket %d (%ld, %ld) at mean value %ld\n", bucket_id,
430 (long)node.min, (long)node.max, (long)mean);
431
432 small_bucket_index = heap_insert(heap, node.min, mean);
433 if(small_bucket_index < 0) {
434 return 0;
435 }
436
437 large_bucket_index = heap_insert(heap, mean + 1, node.max);
438 if(large_bucket_index < 0) {
439 /*heap_remove(small_bucket);*/
440 return 0;
441 }
442
443 return 1;
444}
445
446int
447insert_item(heap_t *heap, maxheap_key_t key, maxheap_value_t value)
448{
449 int heap_iterator;
450 int bucket_id, last_good_bucket_id;
451 struct key_value_pair pair;
452
453 for(heap_iterator = 0, last_good_bucket_id = -1;;) {
454 bucket_id = heap_find(heap, key, &heap_iterator);
455 if(bucket_id < 0) {
456 break;
457 }
458 last_good_bucket_id = bucket_id;
459 }
460 bucket_id = last_good_bucket_id;
461
462 if(bucket_id < 0) {
463 PRINTF("DB: No bucket for key %ld\n", (long)key);
464 return 0;
465 }
466
467 pair.key = key;
468 pair.value = value;
469
470 if(heap->next_free_slot[bucket_id] == BUCKET_SIZE) {
471 PRINTF("DB: Bucket %d is full\n", bucket_id);
472 if(bucket_split(heap, bucket_id) == 0) {
473 return 0;
474 }
475
476 /* Select one of the newly created buckets. */
477 bucket_id = heap_find(heap, key, &heap_iterator);
478 if(bucket_id < 0) {
479 return 0;
480 }
481 }
482
483 if(bucket_append(heap, bucket_id, &pair) == 0) {
484 return 0;
485 }
486
487 PRINTF("DB: Inserted key %ld (hash %ld) into the heap at bucket_id %d\n",
488 (long)key, (long)transform_key(key), bucket_id);
489
490 return 1;
491}
492
493static db_result_t
494create(index_t *index)
495{
496 char heap_filename[DB_MAX_FILENAME_LENGTH];
497 char bucket_filename[DB_MAX_FILENAME_LENGTH];
498 char *filename;
499 db_result_t result;
500 heap_t *heap;
501
502 heap = NULL;
503 filename = NULL;
504 bucket_filename[0] = '\0';
505
506 /* Generate the heap file, which is the main index file that is
507 referenced from the metadata of the relation. */
508 filename = storage_generate_file("heap",
509 (unsigned long)NODE_LIMIT * sizeof(heap_node_t));
510 if(filename == NULL) {
511 PRINTF("DB: Failed to generate a heap file\n");
512 return DB_INDEX_ERROR;
513 }
514
515 memcpy(index->descriptor_file, filename,
516 sizeof(index->descriptor_file));
517
518 PRINTF("DB: Generated the heap file \"%s\" using %lu bytes of space\n",
519 index->descriptor_file, (unsigned long)NODE_LIMIT * sizeof(heap_node_t));
520
521 index->opaque_data = heap = memb_alloc(&heaps);
522 if(heap == NULL) {
523 PRINTF("DB: Failed to allocate a heap\n");
524 result = DB_ALLOCATION_ERROR;
525 goto end;
526 }
527 heap->heap_storage = -1;
528 heap->bucket_storage = -1;
529
530 /* Generate the bucket file, which stores the (key, value) pairs. */
531 filename = storage_generate_file("bucket",
532 (unsigned long)NODE_LIMIT * sizeof(bucket_t));
533 if(filename == NULL) {
534 PRINTF("DB: Failed to generate a bucket file\n");
535 result = DB_INDEX_ERROR;
536 goto end;
537 }
538 memcpy(bucket_filename, filename, sizeof(bucket_filename));
539
540 PRINTF("DB: Generated the bucket file \"%s\" using %lu bytes of space\n",
541 bucket_filename, (unsigned long)NODE_LIMIT * sizeof(bucket_t));
542
543 /* Initialize the heap. */
544 memset(&heap->next_free_slot, 0, sizeof(heap->next_free_slot));
545
546 heap->heap_storage = storage_open(index->descriptor_file);
547 heap->bucket_storage = storage_open(bucket_filename);
548 if(heap->heap_storage < 0 || heap->bucket_storage < 0) {
549 result = DB_STORAGE_ERROR;
550 goto end;
551 }
552
553 if(DB_ERROR(storage_write(heap->heap_storage, &bucket_filename, 0,
554 sizeof(bucket_filename)))) {
555 result = DB_STORAGE_ERROR;
556 goto end;
557 }
558
559 if(heap_insert(heap, KEY_MIN, KEY_MAX) < 0) {
560 PRINTF("DB: Heap insertion error\n");
561 result = DB_INDEX_ERROR;
562 goto end;
563 }
564
565 PRINTF("DB: Created a heap index\n");
566 result = DB_OK;
567
568 end:
569 if(result != DB_OK) {
570 if(heap != NULL) {
571 storage_close(heap->bucket_storage);
572 storage_close(heap->heap_storage);
573 memb_free(&heaps, heap);
574 }
575 if(index->descriptor_file[0] != '\0') {
576 cfs_remove(heap_filename);
577 index->descriptor_file[0] = '\0';
578 }
579 if(bucket_filename[0] != '\0') {
580 cfs_remove(bucket_filename);
581 }
582 }
583 return result;
584}
585
586static db_result_t
587destroy(index_t *index)
588{
589 release(index);
590 return DB_INDEX_ERROR;
591}
592
593static db_result_t
594load(index_t *index)
595{
596 heap_t *heap;
597 db_storage_id_t fd;
598 char bucket_file[DB_MAX_FILENAME_LENGTH];
599
600 index->opaque_data = heap = memb_alloc(&heaps);
601 if(heap == NULL) {
602 PRINTF("DB: Failed to allocate a heap\n");
603 return DB_ALLOCATION_ERROR;
604 }
605
606 fd = storage_open(index->descriptor_file);
607 if(fd < 0) {
608 return DB_STORAGE_ERROR;
609 }
610
611 if(storage_read(fd, bucket_file, 0, sizeof(bucket_file)) !=
612 sizeof(bucket_file)) {
613 storage_close(fd);
614 return DB_STORAGE_ERROR;
615 }
616
617 storage_close(fd);
618
619 heap->heap_storage = storage_open(index->descriptor_file);
620 heap->bucket_storage = storage_open(bucket_file);
621
622 memset(&heap->next_free_slot, 0, sizeof(heap->next_free_slot));
623
624 PRINTF("DB: Loaded max-heap index from file %s and bucket file %s\n",
625 index->descriptor_file, bucket_file);
626
627 return DB_OK;
628}
629
630static db_result_t
631release(index_t *index)
632{
633 heap_t *heap;
634
635 heap = index->opaque_data;
636
637 storage_close(heap->bucket_storage);
638 storage_close(heap->heap_storage);
639 memb_free(&heaps, index->opaque_data);
640 return DB_INDEX_ERROR;
641}
642
643static db_result_t
644insert(index_t *index, attribute_value_t *key, tuple_id_t value)
645{
646 heap_t *heap;
647 long long_key;
648
649 heap = (heap_t *)index->opaque_data;
650
651 long_key = db_value_to_long(key);
652
653 if(insert_item(heap, (maxheap_key_t)long_key,
654 (maxheap_value_t)value) == 0) {
655 PRINTF("DB: Failed to insert key %ld into a max-heap index\n", long_key);
656 return DB_INDEX_ERROR;
657 }
658 return DB_OK;
659}
660
661static db_result_t
662delete(index_t *index, attribute_value_t *value)
663{
664 return DB_INDEX_ERROR;
665}
666
667static tuple_id_t
668get_next(index_iterator_t *iterator)
669{
670 struct iteration_cache {
671 index_iterator_t *index_iterator;
672 int heap_iterator;
673 tuple_id_t found_items;
674 uint8_t start;
675 int visited_buckets[NODE_DEPTH];
676 int end;
677 };
678 static struct iteration_cache cache;
679 heap_t *heap;
680 maxheap_key_t key;
681 int bucket_id;
682 int tmp_heap_iterator;
683 int i;
684 struct bucket_cache *bcache;
685 uint8_t next_free_slot;
686
687 heap = (heap_t *)iterator->index->opaque_data;
688 key = VALUE_INT(&iterator->min_value);
689
690 if(cache.index_iterator != iterator || iterator->next_item_no == 0) {
691 /* Initialize the cache for a new search. */
692 cache.end = NODE_DEPTH - 1;
693 cache.found_items = cache.start = 0;
694 cache.index_iterator = iterator;
695
696 /* Find the downward path through the heap consisting of all nodes
697 that could possibly contain the key. */
698 for(i = tmp_heap_iterator = 0; i < NODE_DEPTH; i++) {
699 cache.visited_buckets[i] = heap_find(heap, key, &tmp_heap_iterator);
700 if(cache.visited_buckets[i] < 0) {
701 cache.end = i - 1;
702 break;
703 }
704 }
705 cache.heap_iterator = cache.end;
706 }
707
708 /*
709 * Search for the key in each heap node, starting from the bottom
710 * of the heap. Because the bottom nodes contain are very narrow
711 * range of keys, there is a much higher chance that the key will be
712 * there rather than at the top.
713 */
714 for(; cache.heap_iterator >= 0; cache.heap_iterator--) {
715 bucket_id = cache.visited_buckets[cache.heap_iterator];
716
717 PRINTF("DB: Find key %lu in bucket %d\n", (unsigned long)key, bucket_id);
718
719 if((bcache = bucket_load(heap, bucket_id)) == NULL) {
720 PRINTF("DB: Failed to load bucket %d\n", bucket_id);
721 return INVALID_TUPLE;
722 }
723
724 /* Because keys are stored in an unsorted order in the bucket, we
725 * need to search the bucket sequentially. */
726 next_free_slot = heap->next_free_slot[bucket_id];
727 for(i = cache.start; i < next_free_slot; i++) {
728 if(bcache->bucket.pairs[i].key == key) {
729 if(cache.found_items++ == iterator->next_item_no) {
730 iterator->next_item_no++;
731 cache.start = i + 1;
732 PRINTF("DB: Found key %ld with value %lu\n", (long)key,
733 (unsigned long)bcache->bucket.pairs[i].value);
734 return (tuple_id_t)bcache->bucket.pairs[i].value;
735 }
736 }
737 }
738 }
739
740 if(VALUE_INT(&iterator->min_value) == VALUE_INT(&iterator->max_value)) {
741 PRINTF("DB: Could not find key %ld in the index\n", (long)key);
742 return INVALID_TUPLE;
743 }
744
745 iterator->next_item_no = 0;
746 VALUE_INT(&iterator->min_value)++;
747
748 return get_next(iterator);
749}
Header for the Coffee file system.
CFS header file.
Header file for the CRC16 calculcation.
Database configuration options.
int cfs_remove(const char *name)
Remove a file.
Definition cfs-cooja.c:147
unsigned short crc16_data(const unsigned char *data, int len, unsigned short acc)
Calculate the CRC16 over a data area.
Definition crc16.c:66
int memb_free(struct memb *m, void *ptr)
Deallocate a memory block from a memory block previously declared with MEMB().
Definition memb.c:78
void * memb_alloc(struct memb *m)
Allocate a memory block from a block of memory declared with MEMB().
Definition memb.c:59
#define MEMB(name, structure, num)
Declare a memory block.
Definition memb.h:91
static void start(void)
Start measurement.
Memory block allocation routines.
Declarations for the result acquisition API.
The storage interface used by the database.
A set of debugging macros for the IP stack.