|
1 // Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // dstcache.cpp - Destination cache implementation |
|
15 // Destination cache implementation |
|
16 // |
|
17 |
|
18 |
|
19 |
|
20 /** |
|
21 @file dstcache.cpp |
|
22 */ |
|
23 |
|
24 #include <in6_dstcache.h> |
|
25 #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS |
|
26 #include <in6_dstcache_internal.h> |
|
27 #endif |
|
28 |
|
29 #ifdef NONSHARABLE_CLASS |
|
30 NONSHARABLE_CLASS(CDestinationCache); |
|
31 #endif |
|
32 |
|
33 class CDestinationCache : public MDestinationCache, public CBase |
|
34 { |
|
35 public: |
|
36 CDestinationCache(TInt aKeyMode); |
|
37 // ~CDestinationCache(); // No explicit destructor needed, as far as I see |
|
38 |
|
39 void ConstructL(); |
|
40 |
|
41 virtual void StoreL(const TInetAddr &aAddr, TCacheInfo &aInfo); |
|
42 |
|
43 virtual const TCacheInfo *Find(const TInetAddr &aAddr); |
|
44 |
|
45 virtual void RemoveL(const TInetAddr &aAddr); |
|
46 |
|
47 virtual void SetL(const TInetAddr &aAddr, TUint aParIndex, TUint32 aValue); |
|
48 |
|
49 virtual void Cleanup(); |
|
50 |
|
51 virtual inline void RemoveAll() |
|
52 { iStorage.RemoveAll(); } |
|
53 |
|
54 virtual inline void SetLifetime(TUint aLifetime) { iLifetime = aLifetime; } |
|
55 |
|
56 virtual inline void SetMaxSize(TUint aMaxSize) { iMaxSize = aMaxSize; } |
|
57 |
|
58 friend TBool IfIsExpired(const TCacheInfo &aCinfo, void *aCachePtr); |
|
59 |
|
60 virtual TBool Match(const TInetAddr& aAddrA, const TInetAddr& aAddrB) const; |
|
61 |
|
62 private: |
|
63 TBool IsExpired(const TCacheInfo &aCinfo) const; |
|
64 |
|
65 TCacheHash iStorage; //< Hash table that stores the destination cache entries |
|
66 TUint iLifetime; //< Lifetime of a destination cache entry (seconds) |
|
67 TUint iMaxSize; //< Maximum memory used for cache entries (bytes) |
|
68 TUint iMemUsed; //< Current total memory used by cache entries |
|
69 |
|
70 // Key Mode selected when instantiating this class |
|
71 THashKeyIp6::TKeyMode iKeyMode; |
|
72 }; |
|
73 |
|
74 |
|
75 EXPORT_C MDestinationCache *MDestinationCache::CreateDstCache(TInt aKeyMode) |
|
76 { |
|
77 CDestinationCache *dcache = new CDestinationCache(aKeyMode); |
|
78 if (dcache) |
|
79 { |
|
80 TInt err = KErrNone; |
|
81 // coverity[leave_without_push] |
|
82 // if ConstructL() leaves, the leave code is trapped in "err" and proper cleanup is done by freeing the memory alocated to "dcache" |
|
83 TRAP(err, dcache->ConstructL()); |
|
84 if (err != KErrNone) |
|
85 { |
|
86 delete dcache; |
|
87 dcache = NULL; |
|
88 } |
|
89 } |
|
90 return dcache; |
|
91 } |
|
92 |
|
93 |
|
94 CDestinationCache::CDestinationCache(TInt aKeyMode) : CBase(), |
|
95 iStorage(KCacheHashSize), |
|
96 iLifetime(KDstCacheLifetime), |
|
97 iMaxSize(KDstCacheMaxSize) |
|
98 { |
|
99 switch (aKeyMode) |
|
100 { |
|
101 case 2: |
|
102 iKeyMode = THashKeyIp6::EPerNet; |
|
103 break; |
|
104 |
|
105 default: |
|
106 iKeyMode = THashKeyIp6::EPerHost; |
|
107 break; |
|
108 |
|
109 // EPerIface not yet implemented |
|
110 } |
|
111 } |
|
112 |
|
113 |
|
114 void CDestinationCache::ConstructL() |
|
115 { |
|
116 iStorage.ConstructL(); |
|
117 } |
|
118 |
|
119 |
|
120 void CDestinationCache::StoreL(const TInetAddr &aAddr, TCacheInfo &aInfo) |
|
121 { |
|
122 aInfo.iStoreTime.UniversalTime(); |
|
123 THashKeyIp6 k(iKeyMode, aAddr); |
|
124 |
|
125 // Check that hash size does not exceed the given limit. |
|
126 // If it is about to do so, clean up expired entries and try again. |
|
127 // If it still fails, StoreL fails. |
|
128 if (iMemUsed >= iMaxSize) |
|
129 { |
|
130 Cleanup(); // This reduces iMemUsed if there was expired entries |
|
131 if (iMemUsed >= iMaxSize) |
|
132 { |
|
133 User::Leave(KErrNoMemory); // TODO: change error code |
|
134 } |
|
135 } |
|
136 |
|
137 // Check if there is a non-expired entry in the cache. If incoming aInfo has any |
|
138 // non-null entries, they replace the earlier cache entries. Entries that were |
|
139 // not updated with this call remain unmodified in the cache. |
|
140 // However, the store time is refreshed. |
|
141 TCacheInfo *oldent = iStorage.Find(k); |
|
142 if (oldent && !IsExpired(*oldent)) |
|
143 { |
|
144 for (TUint i = 0; i < KNumCacheMetrics; i++) |
|
145 { |
|
146 if (aInfo.iMetrics[i] != 0) |
|
147 oldent->iMetrics[i] = aInfo.iMetrics[i]; |
|
148 } |
|
149 oldent->iStoreTime.UniversalTime(); |
|
150 |
|
151 return; // Hash-store method is not needed this time. |
|
152 } |
|
153 |
|
154 TUint mem = iStorage.StoreL(k, aInfo); |
|
155 iMemUsed += mem; |
|
156 } |
|
157 |
|
158 |
|
159 void CDestinationCache::SetL(const TInetAddr &aAddr, TUint aParIndex, TUint32 aValue) |
|
160 { |
|
161 TCacheInfo *infoptr; |
|
162 |
|
163 // Overwrite existing value if it exists, otherwise create a new entry in cache |
|
164 THashKeyIp6 k(iKeyMode, aAddr); |
|
165 infoptr = iStorage.Find(k); |
|
166 |
|
167 if (infoptr && !IsExpired(*infoptr)) |
|
168 { |
|
169 infoptr->iMetrics[aParIndex] = aValue; |
|
170 infoptr->iStoreTime.UniversalTime(); |
|
171 } |
|
172 else |
|
173 { |
|
174 TCacheInfo newinfo; |
|
175 newinfo.ClearAll(); |
|
176 newinfo.iMetrics[aParIndex] = aValue; |
|
177 StoreL(aAddr, newinfo); |
|
178 } |
|
179 } |
|
180 |
|
181 |
|
182 const TCacheInfo *CDestinationCache::Find(const TInetAddr &aAddr) |
|
183 { |
|
184 THashKeyIp6 k(iKeyMode, aAddr); |
|
185 TCacheInfo *cinfo = iStorage.Find(k); |
|
186 |
|
187 // Check whether the cache entry is recent enough. If not, return NULL. |
|
188 // However, we don't delete an expired entry from cache. It remains as a placeholder |
|
189 // for future equivalent entries. |
|
190 if (!cinfo || IsExpired(*cinfo)) |
|
191 { |
|
192 return NULL; |
|
193 } |
|
194 |
|
195 return cinfo; |
|
196 } |
|
197 |
|
198 |
|
199 void CDestinationCache::RemoveL(const TInetAddr &aAddr) |
|
200 { |
|
201 THashKeyIp6 k(iKeyMode, aAddr); |
|
202 TUint mem = iStorage.RemoveL(k); |
|
203 iMemUsed -= mem; |
|
204 } |
|
205 |
|
206 |
|
207 // Same as CDestinationCache::IsExpired(), but this is to be used with RemoveIf() call. |
|
208 TBool IfIsExpired(const TCacheInfo &aCinfo, void *aCachePtr) |
|
209 { |
|
210 if (!aCachePtr) |
|
211 { |
|
212 return EFalse; |
|
213 } |
|
214 return ((CDestinationCache *)aCachePtr)->IsExpired(aCinfo); |
|
215 } |
|
216 |
|
217 |
|
218 void CDestinationCache::Cleanup() |
|
219 { |
|
220 iMemUsed -= iStorage.RemoveIf(IfIsExpired, this); |
|
221 } |
|
222 |
|
223 |
|
224 TBool CDestinationCache::IsExpired(const TCacheInfo &aCinfo) const |
|
225 { |
|
226 TTime now; |
|
227 TTimeIntervalSeconds secs; |
|
228 |
|
229 now.UniversalTime(); |
|
230 if (now.SecondsFrom(aCinfo.iStoreTime, secs) != KErrNone) |
|
231 { |
|
232 // Overflow or something. Consider it as expired entry. |
|
233 return ETrue; |
|
234 } |
|
235 |
|
236 if ((TUint)secs.Int() > iLifetime) |
|
237 { |
|
238 // Expired |
|
239 return ETrue; |
|
240 } |
|
241 |
|
242 return EFalse; |
|
243 } |
|
244 |
|
245 |
|
246 TBool CDestinationCache::Match(const TInetAddr& aAddrA, const TInetAddr& aAddrB) const |
|
247 { |
|
248 THashKeyIp6 ka(iKeyMode, aAddrA); |
|
249 THashKeyIp6 kb(iKeyMode, aAddrB); |
|
250 |
|
251 return ka.IsEqual(kb); |
|
252 } |
|
253 |
|
254 |
|
255 // --- THashTable methods --- |
|
256 |
|
257 template <class K, class V> |
|
258 THashTable<K,V>::THashTable(TUint aSize) : iTable(NULL), iSize(aSize) |
|
259 { |
|
260 } |
|
261 |
|
262 |
|
263 template <class K, class V> |
|
264 THashTable<K,V>::~THashTable() |
|
265 { |
|
266 RemoveAll(); |
|
267 delete [] iTable; |
|
268 } |
|
269 |
|
270 |
|
271 template <class K, class V> |
|
272 void THashTable<K,V>::ConstructL() |
|
273 { |
|
274 iTable = new TChain<K,V>[iSize]; |
|
275 if (!iTable) |
|
276 { |
|
277 User::Leave(KErrNoMemory); |
|
278 } |
|
279 |
|
280 for (TUint i = 0; i < iSize; i++) |
|
281 { |
|
282 iTable[i].iNext = NULL; |
|
283 } |
|
284 } |
|
285 |
|
286 |
|
287 template <class K, class V> |
|
288 TUint THashTable<K,V>::StoreL(MHashKey& aKey, V& aValue) |
|
289 { |
|
290 if (!iTable) |
|
291 { |
|
292 User::Leave(KErrGeneral); |
|
293 } |
|
294 |
|
295 TUint memused = 0; |
|
296 TInt idx = aKey.ToInt() % iSize; |
|
297 TChain<K,V> *ptr = &iTable[idx], *newitem = NULL; |
|
298 while (ptr->iNext) |
|
299 { |
|
300 if (ptr->iNext->iKey.IsEqual(aKey)) |
|
301 { |
|
302 // The conventional mode would be: |
|
303 // User::Leave(KErrAlreadyExists) |
|
304 // ...however, due to the lazy garbage collection mode adopted, we overwrite |
|
305 // the existing entry |
|
306 newitem = ptr->iNext; |
|
307 break; |
|
308 } |
|
309 ptr = ptr->iNext; |
|
310 } |
|
311 |
|
312 // If the key wasn't stored earlier, allocate space for new data item. |
|
313 // Allocations from heap is not always a very popular idea, but these events are |
|
314 // expected to occur quite rarely. |
|
315 if (!newitem) |
|
316 { |
|
317 newitem = new(ELeave) TChain<K,V>; |
|
318 |
|
319 if (!newitem) |
|
320 { |
|
321 User::Leave(KErrNoMemory); |
|
322 } |
|
323 |
|
324 newitem->iKey = *((K*) &aKey); |
|
325 newitem->iNext = NULL; |
|
326 ptr->iNext = newitem; |
|
327 memused = sizeof(TChain<K,V>); |
|
328 } |
|
329 |
|
330 newitem->iValue = aValue; |
|
331 |
|
332 /* Dynamically allocated memory newitem has been assigned to ptr->iNext and managed through the list. |
|
333 So CleanupStack::PopAndDestroy() will deallocate that memory. But, Coverity has misinterpreted it an issue.*/ |
|
334 // coverity [SYMBIAN.CLEANUP STACK] |
|
335 // coverity [memory_leak] |
|
336 return memused; |
|
337 } |
|
338 |
|
339 |
|
340 template <class K, class V> |
|
341 V* THashTable<K,V>::Find(MHashKey& aKey) |
|
342 { |
|
343 if (!iTable) return NULL; |
|
344 |
|
345 TInt idx = aKey.ToInt() % iSize; |
|
346 TChain<K,V> *ptr = &iTable[idx]; |
|
347 while (ptr->iNext) |
|
348 { |
|
349 if (ptr->iNext->iKey.IsEqual(aKey)) |
|
350 { |
|
351 return &ptr->iNext->iValue; |
|
352 } |
|
353 ptr = ptr->iNext; |
|
354 } |
|
355 |
|
356 // Not found |
|
357 return NULL; |
|
358 } |
|
359 |
|
360 |
|
361 template <class K, class V> |
|
362 TUint THashTable<K,V>::RemoveL(MHashKey& aKey) |
|
363 { |
|
364 if (!iTable) |
|
365 { |
|
366 User::Leave(KErrGeneral); |
|
367 } |
|
368 |
|
369 TInt idx = aKey.ToInt() % iSize; |
|
370 TChain<K,V> *ptr = &iTable[idx]; |
|
371 while (ptr->iNext) |
|
372 { |
|
373 if (ptr->iNext->iKey.IsEqual(aKey)) |
|
374 { |
|
375 TChain<K,V> *removed = ptr->iNext; |
|
376 ptr->iNext = removed->iNext; |
|
377 delete removed; |
|
378 return sizeof(TChain<K,V>); |
|
379 } |
|
380 } |
|
381 |
|
382 // Key was not found |
|
383 User::Leave(KErrNotFound); |
|
384 return 0; |
|
385 } |
|
386 |
|
387 |
|
388 template <class K, class V> |
|
389 TUint THashTable<K,V>::Length() |
|
390 { |
|
391 if (!iTable) return 0; |
|
392 |
|
393 TUint length = 0; |
|
394 |
|
395 for (TInt idx = 0; idx < iSize; idx++) |
|
396 { |
|
397 TChain<K,V> *ptr = &iTable[idx]; |
|
398 while (ptr->iNext) |
|
399 { |
|
400 length++; |
|
401 ptr = ptr->iNext; |
|
402 } |
|
403 } |
|
404 return length; |
|
405 } |
|
406 |
|
407 |
|
408 template <class K, class V> |
|
409 TUint THashTable<K,V>::RemoveIf( TBool (*aRemoveCriteria)(const V&, void *), |
|
410 void *aDataObject) |
|
411 { |
|
412 if (!iTable) |
|
413 { |
|
414 return 0; |
|
415 } |
|
416 |
|
417 TUint memfreed = 0; |
|
418 TChain<K,V> *ptr = NULL; |
|
419 for (TUint idx = 0; idx < iSize; idx++) |
|
420 { |
|
421 ptr = &iTable[idx]; |
|
422 while (ptr->iNext) |
|
423 { |
|
424 if (aRemoveCriteria(ptr->iNext->iValue, aDataObject)) |
|
425 { |
|
426 TChain<K,V> *removed = ptr->iNext; |
|
427 ptr->iNext = removed->iNext; |
|
428 delete removed; |
|
429 memfreed += sizeof(TChain<K,V>); |
|
430 } |
|
431 ptr = ptr->iNext; |
|
432 } |
|
433 } |
|
434 |
|
435 return memfreed; |
|
436 } |
|
437 |
|
438 |
|
439 template <class K, class V> |
|
440 void THashTable<K,V>::RemoveAll() |
|
441 { |
|
442 if (iTable != NULL) |
|
443 { |
|
444 TChain<K,V> *ptr = NULL, *last = NULL; |
|
445 for (TUint i = 0; i < iSize; i++) |
|
446 { |
|
447 // Remember that the first chain entry in &iTable[i] does not contain data. |
|
448 // The data items start from the linked entries. |
|
449 ptr = &iTable[i]; |
|
450 while (ptr) |
|
451 { |
|
452 ptr = ptr->iNext; |
|
453 if (last) delete last; |
|
454 last = ptr; |
|
455 } |
|
456 } |
|
457 } |
|
458 } |
|
459 |
|
460 |
|
461 THashKeyIp6::THashKeyIp6(TKeyMode aKeyMode, const TInetAddr& aAddr) |
|
462 { |
|
463 // TODO: retrieve key mode, and assign only a prefix instead of full address. |
|
464 // Simple prefix length is not enough, because I'd like to use the same cache for |
|
465 // both IPv4 and IPv6 |
|
466 iKeyMode = aKeyMode; |
|
467 iAddress = aAddr.Ip6Address(); |
|
468 iScopeId = aAddr.Scope(); |
|
469 } |
|
470 |
|
471 |
|
472 TUint THashKeyIp6::ToInt() |
|
473 { |
|
474 if (iKeyMode == EPerNet) |
|
475 { |
|
476 if (iAddress.IsV4Mapped()) |
|
477 { |
|
478 // IPv4: Network is considered a /24 |
|
479 return iAddress.u.iAddr32[3] >> 8; |
|
480 } |
|
481 else |
|
482 { |
|
483 // IPv6: Network is considered a /64. |
|
484 // Return the least significant 32 bits from the network part. |
|
485 return iAddress.u.iAddr32[1]; |
|
486 } |
|
487 } |
|
488 else // EPerHost and others (EPerIface not yet implemented) |
|
489 { |
|
490 // The least significant 32 bits is used as the int index |
|
491 return iAddress.u.iAddr32[3]; |
|
492 } |
|
493 } |
|
494 |
|
495 |
|
496 TBool THashKeyIp6::IsEqual(const MHashKey& aKey) |
|
497 { |
|
498 // casting is always risky business, but the user is required |
|
499 // to use this method properly |
|
500 const TIp6Addr addr2 = ((const THashKeyIp6&)aKey).Ip6Address(); |
|
501 |
|
502 // If scope id is different, match will always fail |
|
503 // (until per interface mode is implemented) |
|
504 if (iScopeId != ((const THashKeyIp6&)aKey).ScopeId()) |
|
505 { |
|
506 return EFalse; |
|
507 } |
|
508 |
|
509 if (iKeyMode == EPerNet) |
|
510 { |
|
511 TInt match = iAddress.Match(addr2); |
|
512 if (iAddress.IsV4Mapped() && match >= 96 + 24) |
|
513 { |
|
514 return ETrue; |
|
515 } |
|
516 else if (match >= 64) |
|
517 { |
|
518 return ETrue; |
|
519 } |
|
520 return EFalse; |
|
521 } |
|
522 else // EPerHost and others (EPerIface not yet implemented) |
|
523 { |
|
524 return iAddress.IsEqual(addr2); |
|
525 } |
|
526 } |