blob: dc5d3ddeaa9307b148e792cb36608a43c168fed1 [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"
Radek Krejcida04f4a2015-05-21 12:54:09 +020029#include "context.h"
Radek Krejciee627582015-04-20 17:39:59 +020030
Radek Krejcida04f4a2015-05-21 12:54:09 +020031void lydict_init(struct dict_table *dict)
32{
33 if (!dict) {
34 ly_errno = LY_EINVAL;
35 return;
36 }
Radek Krejciee627582015-04-20 17:39:59 +020037
Radek Krejcida04f4a2015-05-21 12:54:09 +020038 dict->hash_mask = DICT_SIZE - 1;
39}
40
41void lydict_clean(struct dict_table *dict)
42{
43 int i;
44 struct dict_rec *chain, *rec;
45
46 if (!dict) {
47 ly_errno = LY_EINVAL;
48 return;
49 }
50
51 for (i = 0; i < DICT_SIZE; i++) {
52 rec = &dict->recs[i];
53 chain = rec->next;
54
55 free(rec->value);
56 while(chain) {
57 rec = chain;
58 chain = rec->next;
59
60 free(rec->value);
61 free(rec);
62 }
63 }
64}
Radek Krejciee627582015-04-20 17:39:59 +020065
66/*
67 * Bob Jenkin's one-at-a-time hash
68 * http://www.burtleburtle.net/bob/hash/doobs.html
69 *
70 * Spooky hash is faster, but it works only for little endian architectures.
71 */
72static uint32_t dict_hash(const char *key, size_t len)
73{
74 uint32_t hash, i;
75
76 for(hash = i = 0; i < len; ++i)
77 {
78 hash += key[i];
79 hash += (hash << 10);
80 hash ^= (hash >> 6);
81 }
82 hash += (hash << 3);
83 hash ^= (hash >> 11);
84 hash += (hash << 15);
85 return hash;
86}
87
Radek Krejcida04f4a2015-05-21 12:54:09 +020088void lydict_remove(struct ly_ctx *ctx, const char *value)
Radek Krejciee627582015-04-20 17:39:59 +020089{
90 size_t len;
91 uint32_t index;
92 struct dict_rec *record, *prev = NULL;
93
Radek Krejcida04f4a2015-05-21 12:54:09 +020094 if (!ctx || !value) {
Radek Krejciee627582015-04-20 17:39:59 +020095 return;
96 }
97
98 len = strlen(value);
99
Radek Krejcida04f4a2015-05-21 12:54:09 +0200100 index = dict_hash(value, len) & ctx->dict.hash_mask;
101 record = &ctx->dict.recs[index];
Radek Krejciee627582015-04-20 17:39:59 +0200102
103 while (record && record->value != value) {
104 prev = record;
105 record = record->next;
106 }
107
108 if (!record) {
109 /* record not found */
110 return;
111 }
112
113 record->refcount--;
114 if (!record->refcount) {
115 free(record->value);
116 if (record->next) {
117 if (prev) {
118 /* change in dynamically allocated chain */
Radek Krejci4ea08382015-04-21 09:41:40 +0200119 prev->next = record->next;
Radek Krejciee627582015-04-20 17:39:59 +0200120 free(record);
121 } else {
122 /* move dynamically allocated record into the static array */
123 prev = record->next; /* temporary storage */
124 memcpy(record, record->next, sizeof *record);
125 free(prev);
126 }
127 } else if (prev) {
128 /* removing last record from the dynamically allocated chain */
129 prev->next = NULL;
130 free(record);
Radek Krejcida04f4a2015-05-21 12:54:09 +0200131 } else {
132 /* clean the static record content */
133 memset(record, 0, sizeof *record);
Radek Krejciee627582015-04-20 17:39:59 +0200134 }
135 }
136}
137
Radek Krejcida04f4a2015-05-21 12:54:09 +0200138static char *dict_insert(struct ly_ctx *ctx, char *value, size_t len, int zerocopy)
Radek Krejciee627582015-04-20 17:39:59 +0200139{
140 uint32_t index;
141 struct dict_rec *record, *new;
142
Radek Krejcida04f4a2015-05-21 12:54:09 +0200143 index = dict_hash(value, len) & ctx->dict.hash_mask;
144 record = &ctx->dict.recs[index];
Radek Krejciee627582015-04-20 17:39:59 +0200145
146 if (!record->value) {
147 /* first record with this hash */
Radek Krejci4ea08382015-04-21 09:41:40 +0200148 if (zerocopy) {
149 record->value = value;
150 } else {
151 record->value = malloc((len + 1) * sizeof *record->value);
152 memcpy(record->value, value, len);
153 record->value[len] = '\0';
154 }
Radek Krejciee627582015-04-20 17:39:59 +0200155 record->refcount = 1;
156 record->next = NULL;
157
Radek Krejci4ea08382015-04-21 09:41:40 +0200158 LY_DBG("DICT: inserting \"%s\"", record->value);
Radek Krejciee627582015-04-20 17:39:59 +0200159 return record->value;
160 }
161
162 /* collision, search if the value is already in dict */
163 while (record) {
164 if (!memcmp(value, record->value, len) && record->value[len] == '\0') {
165 /* record found */
166 record->refcount++;
Radek Krejci4ea08382015-04-21 09:41:40 +0200167
168 if (zerocopy) {
169 free(value);
170 }
171
172 LY_DBG("DICT: inserting (refcount) \"%s\"", record->value);
Radek Krejciee627582015-04-20 17:39:59 +0200173 return record->value;
174 }
175
176 if (!record->next) {
177 /* not present, add as a new record in chain */
178 break;
179 }
180
181 record = record->next;
182 }
183
Radek Krejci4ea08382015-04-21 09:41:40 +0200184 /* create new record and add it behind the last record */
Radek Krejciee627582015-04-20 17:39:59 +0200185 new = malloc(sizeof *record);
Radek Krejci4ea08382015-04-21 09:41:40 +0200186 if (zerocopy) {
187 new->value = value;
188 } else {
189 new->value = malloc((len + 1) * sizeof *record->value);
190 memcpy(new->value, value, len);
191 new->value[len] = '\0';
192 }
Radek Krejciee627582015-04-20 17:39:59 +0200193 new->refcount = 1;
Radek Krejci4ea08382015-04-21 09:41:40 +0200194 new->next = NULL;
Radek Krejciee627582015-04-20 17:39:59 +0200195
196 record->next = new;
197
Radek Krejci4ea08382015-04-21 09:41:40 +0200198 LY_DBG("DICT: inserting \"%s\" with collision ", record->value);
Radek Krejciee627582015-04-20 17:39:59 +0200199 return new->value;
200}
Radek Krejci4ea08382015-04-21 09:41:40 +0200201
Radek Krejcida04f4a2015-05-21 12:54:09 +0200202char *lydict_insert(struct ly_ctx *ctx, const char *value, size_t len)
Radek Krejci4ea08382015-04-21 09:41:40 +0200203{
Radek Krejci674e1f82015-04-21 14:12:19 +0200204 if (!value || !len) {
205 return NULL;
206 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200207 return dict_insert(ctx, (char *)value, len, 0);
Radek Krejci4ea08382015-04-21 09:41:40 +0200208}
209
Radek Krejcida04f4a2015-05-21 12:54:09 +0200210char *lydict_insert_zc(struct ly_ctx *ctx, char *value)
Radek Krejci4ea08382015-04-21 09:41:40 +0200211{
Radek Krejci674e1f82015-04-21 14:12:19 +0200212 int len;
213
214 if (!value || !(len = strlen(value))) {
215 return NULL;
216 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200217 return dict_insert(ctx, (char *)value, len, 1);
Radek Krejci4ea08382015-04-21 09:41:40 +0200218}