blob: 2cec4233263c67e5f1e983345251229542d09a2c [file] [log] [blame]
Radek Krejciee627582015-04-20 17:39:59 +02001/**
2 * @file dict.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief libyang dictionary for storing strings
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of the Company nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 */
21
22#include <string.h>
23#include <stdint.h>
24#include <stdlib.h>
25
26
27#include "common.h"
28#include "dict.h"
29
30struct dict_rec {
31 char *value;
32 uint32_t refcount;
33 struct dict_rec *next;
34};
35
36#define DICT_SIZE 1024
37struct dict_table {
38 struct dict_rec recs[DICT_SIZE];
39 int hash_mask;
40 uint32_t used;
41};
42static struct dict_table dict = {{{ 0 }}, DICT_SIZE - 1, 0};
43
44/*
45 * Bob Jenkin's one-at-a-time hash
46 * http://www.burtleburtle.net/bob/hash/doobs.html
47 *
48 * Spooky hash is faster, but it works only for little endian architectures.
49 */
50static uint32_t dict_hash(const char *key, size_t len)
51{
52 uint32_t hash, i;
53
54 for(hash = i = 0; i < len; ++i)
55 {
56 hash += key[i];
57 hash += (hash << 10);
58 hash ^= (hash >> 6);
59 }
60 hash += (hash << 3);
61 hash ^= (hash >> 11);
62 hash += (hash << 15);
63 return hash;
64}
65
Radek Krejci4ea08382015-04-21 09:41:40 +020066void lydict_remove(const char *value)
Radek Krejciee627582015-04-20 17:39:59 +020067{
68 size_t len;
69 uint32_t index;
70 struct dict_rec *record, *prev = NULL;
71
72 if (!value) {
73 return;
74 }
75
76 len = strlen(value);
77
78 index = dict_hash(value, len) & dict.hash_mask;
79 record = &dict.recs[index];
80
81 while (record && record->value != value) {
82 prev = record;
83 record = record->next;
84 }
85
86 if (!record) {
87 /* record not found */
88 return;
89 }
90
91 record->refcount--;
92 if (!record->refcount) {
93 free(record->value);
94 if (record->next) {
95 if (prev) {
96 /* change in dynamically allocated chain */
Radek Krejci4ea08382015-04-21 09:41:40 +020097 prev->next = record->next;
Radek Krejciee627582015-04-20 17:39:59 +020098 free(record);
99 } else {
100 /* move dynamically allocated record into the static array */
101 prev = record->next; /* temporary storage */
102 memcpy(record, record->next, sizeof *record);
103 free(prev);
104 }
105 } else if (prev) {
106 /* removing last record from the dynamically allocated chain */
107 prev->next = NULL;
108 free(record);
109 }
110 }
111}
112
Radek Krejci4ea08382015-04-21 09:41:40 +0200113static char *dict_insert(char *value, size_t len, int zerocopy)
Radek Krejciee627582015-04-20 17:39:59 +0200114{
115 uint32_t index;
116 struct dict_rec *record, *new;
117
Radek Krejciee627582015-04-20 17:39:59 +0200118 index = dict_hash(value, len) & dict.hash_mask;
119 record = &dict.recs[index];
120
121 if (!record->value) {
122 /* first record with this hash */
Radek Krejci4ea08382015-04-21 09:41:40 +0200123 if (zerocopy) {
124 record->value = value;
125 } else {
126 record->value = malloc((len + 1) * sizeof *record->value);
127 memcpy(record->value, value, len);
128 record->value[len] = '\0';
129 }
Radek Krejciee627582015-04-20 17:39:59 +0200130 record->refcount = 1;
131 record->next = NULL;
132
Radek Krejci4ea08382015-04-21 09:41:40 +0200133 LY_DBG("DICT: inserting \"%s\"", record->value);
Radek Krejciee627582015-04-20 17:39:59 +0200134 return record->value;
135 }
136
137 /* collision, search if the value is already in dict */
138 while (record) {
139 if (!memcmp(value, record->value, len) && record->value[len] == '\0') {
140 /* record found */
141 record->refcount++;
Radek Krejci4ea08382015-04-21 09:41:40 +0200142
143 if (zerocopy) {
144 free(value);
145 }
146
147 LY_DBG("DICT: inserting (refcount) \"%s\"", record->value);
Radek Krejciee627582015-04-20 17:39:59 +0200148 return record->value;
149 }
150
151 if (!record->next) {
152 /* not present, add as a new record in chain */
153 break;
154 }
155
156 record = record->next;
157 }
158
Radek Krejci4ea08382015-04-21 09:41:40 +0200159 /* create new record and add it behind the last record */
Radek Krejciee627582015-04-20 17:39:59 +0200160 new = malloc(sizeof *record);
Radek Krejci4ea08382015-04-21 09:41:40 +0200161 if (zerocopy) {
162 new->value = value;
163 } else {
164 new->value = malloc((len + 1) * sizeof *record->value);
165 memcpy(new->value, value, len);
166 new->value[len] = '\0';
167 }
Radek Krejciee627582015-04-20 17:39:59 +0200168 new->refcount = 1;
Radek Krejci4ea08382015-04-21 09:41:40 +0200169 new->next = NULL;
Radek Krejciee627582015-04-20 17:39:59 +0200170
171 record->next = new;
172
Radek Krejci4ea08382015-04-21 09:41:40 +0200173 LY_DBG("DICT: inserting \"%s\" with collision ", record->value);
Radek Krejciee627582015-04-20 17:39:59 +0200174 return new->value;
175}
Radek Krejci4ea08382015-04-21 09:41:40 +0200176
177char *lydict_insert(const char *value, size_t len)
178{
Radek Krejci674e1f82015-04-21 14:12:19 +0200179 if (!value || !len) {
180 return NULL;
181 }
Radek Krejci4ea08382015-04-21 09:41:40 +0200182 return dict_insert((char *)value, len, 0);
183}
184
185char *lydict_insert_zc(char *value)
186{
Radek Krejci674e1f82015-04-21 14:12:19 +0200187 int len;
188
189 if (!value || !(len = strlen(value))) {
190 return NULL;
191 }
192 return dict_insert((char *)value, len, 1);
Radek Krejci4ea08382015-04-21 09:41:40 +0200193}