[NETAPI]
[reactos.git] / reactos / dll / win32 / netapi32 / nbnamecache.c
1 /* Copyright (c) 2003 Juan Lang
2 *
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
16 *
17 * This implementation uses a linked list, because I don't have a decent
18 * hash table implementation handy. This is somewhat inefficient, but it's
19 * rather more efficient than not having a name cache at all.
20 */
21
22 #include "netapi32.h"
23
24 typedef struct _NBNameCacheNode
25 {
26 DWORD expireTime;
27 NBNameCacheEntry *entry;
28 struct _NBNameCacheNode *next;
29 } NBNameCacheNode;
30
31 struct NBNameCache
32 {
33 HANDLE heap;
34 CRITICAL_SECTION cs;
35 DWORD entryExpireTimeMS;
36 NBNameCacheNode *head;
37 };
38
39 /* Unlinks the node pointed to by *prev, and frees any associated memory.
40 * If that node's next pointed to another node, *prev now points to it.
41 * Assumes the caller owns cache's lock.
42 */
43 static void NBNameCacheUnlinkNode(struct NBNameCache *cache,
44 NBNameCacheNode **prev)
45 {
46 if (cache && prev && *prev)
47 {
48 NBNameCacheNode *next = (*prev)->next;
49
50 HeapFree(cache->heap, 0, (*prev)->entry);
51 HeapFree(cache->heap, 0, *prev);
52 *prev = next;
53 }
54 }
55
56 /* Walks the list beginning with cache->head looking for the node with name
57 * name. If the node is found, returns a pointer to the next pointer of the
58 * node _prior_ to the found node (or head if head points to it). Thus, if the
59 * node's all you want, dereference the return value twice. If you want to
60 * modify the list, modify the referent of the return value.
61 * While it's at it, deletes nodes whose time has expired (except the node
62 * you're looking for, of course).
63 * Returns NULL if the node isn't found.
64 * Assumes the caller owns cache's lock.
65 */
66 static NBNameCacheNode **NBNameCacheWalk(struct NBNameCache *cache,
67 const char name[NCBNAMSZ])
68 {
69 NBNameCacheNode **ret = NULL;
70
71 if (cache && cache->head)
72 {
73 NBNameCacheNode **ptr;
74
75 ptr = &cache->head;
76 while (ptr && *ptr && (*ptr)->entry)
77 {
78 if (!memcmp((*ptr)->entry->name, name, NCBNAMSZ - 1))
79 ret = ptr;
80 else
81 {
82 if (GetTickCount() > (*ptr)->expireTime)
83 NBNameCacheUnlinkNode(cache, ptr);
84 }
85 if (*ptr)
86 ptr = &(*ptr)->next;
87 }
88 }
89 return ret;
90 }
91
92 struct NBNameCache *NBNameCacheCreate(HANDLE heap, DWORD entryExpireTimeMS)
93 {
94 struct NBNameCache *cache;
95
96
97 if (!heap)
98 heap = GetProcessHeap();
99 cache = HeapAlloc(heap, 0, sizeof(struct NBNameCache));
100 if (cache)
101 {
102 cache->heap = heap;
103 InitializeCriticalSection(&cache->cs);
104 cache->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": NBNameCache.cs");
105 cache->entryExpireTimeMS = entryExpireTimeMS;
106 cache->head = NULL;
107 }
108 return cache;
109 }
110
111 BOOL NBNameCacheAddEntry(struct NBNameCache *cache, NBNameCacheEntry *entry)
112 {
113 BOOL ret;
114
115 if (cache && entry)
116 {
117 NBNameCacheNode **node;
118
119 EnterCriticalSection(&cache->cs);
120 node = NBNameCacheWalk(cache, (char*)entry->name);
121 if (node)
122 {
123 (*node)->expireTime = GetTickCount() +
124 cache->entryExpireTimeMS;
125 HeapFree(cache->heap, 0, (*node)->entry);
126 (*node)->entry = entry;
127 ret = TRUE;
128 }
129 else
130 {
131 NBNameCacheNode *newNode = HeapAlloc(cache->heap, 0, sizeof(NBNameCacheNode));
132 if (newNode)
133 {
134 newNode->expireTime = GetTickCount() +
135 cache->entryExpireTimeMS;
136 newNode->entry = entry;
137 newNode->next = cache->head;
138 cache->head = newNode;
139 ret = TRUE;
140 }
141 else
142 ret = FALSE;
143 }
144 LeaveCriticalSection(&cache->cs);
145 }
146 else
147 ret = FALSE;
148 return ret;
149 }
150
151 const NBNameCacheEntry *NBNameCacheFindEntry(struct NBNameCache *cache,
152 const UCHAR name[NCBNAMSZ])
153 {
154 const NBNameCacheEntry *ret;
155 UCHAR printName[NCBNAMSZ];
156
157 memcpy(printName, name, NCBNAMSZ - 1);
158 printName[NCBNAMSZ - 1] = '\0';
159 if (cache)
160 {
161 NBNameCacheNode **node;
162
163 EnterCriticalSection(&cache->cs);
164 node = NBNameCacheWalk(cache, (const char *)name);
165 if (node)
166 ret = (*node)->entry;
167 else
168 ret = NULL;
169 LeaveCriticalSection(&cache->cs);
170 }
171 else
172 ret = NULL;
173 return ret;
174 }
175
176 void NBNameCacheDestroy(struct NBNameCache *cache)
177 {
178 if (cache)
179 {
180 cache->cs.DebugInfo->Spare[0] = 0;
181 DeleteCriticalSection(&cache->cs);
182 while (cache->head)
183 NBNameCacheUnlinkNode(cache, &cache->head);
184 HeapFree(cache->heap, 0, cache);
185 }
186 }