Mercurial > hg > gpsynth
comparison third_party/json/json_internalmap.inl @ 0:add35537fdbb tip
Initial import
author | irh <ian.r.hobson@gmail.com> |
---|---|
date | Thu, 25 Aug 2011 11:05:55 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:add35537fdbb |
---|---|
1 // Copyright 2007-2010 Baptiste Lepilleur | |
2 // Distributed under MIT license, or public domain if desired and | |
3 // recognized in your jurisdiction. | |
4 // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE | |
5 | |
6 // included by json_value.cpp | |
7 | |
8 namespace Json { | |
9 | |
10 // ////////////////////////////////////////////////////////////////// | |
11 // ////////////////////////////////////////////////////////////////// | |
12 // ////////////////////////////////////////////////////////////////// | |
13 // class ValueInternalMap | |
14 // ////////////////////////////////////////////////////////////////// | |
15 // ////////////////////////////////////////////////////////////////// | |
16 // ////////////////////////////////////////////////////////////////// | |
17 | |
18 /** \internal MUST be safely initialized using memset( this, 0, sizeof(ValueInternalLink) ); | |
19 * This optimization is used by the fast allocator. | |
20 */ | |
21 ValueInternalLink::ValueInternalLink() | |
22 : previous_( 0 ) | |
23 , next_( 0 ) | |
24 { | |
25 } | |
26 | |
27 ValueInternalLink::~ValueInternalLink() | |
28 { | |
29 for ( int index =0; index < itemPerLink; ++index ) | |
30 { | |
31 if ( !items_[index].isItemAvailable() ) | |
32 { | |
33 if ( !items_[index].isMemberNameStatic() ) | |
34 free( keys_[index] ); | |
35 } | |
36 else | |
37 break; | |
38 } | |
39 } | |
40 | |
41 | |
42 | |
43 ValueMapAllocator::~ValueMapAllocator() | |
44 { | |
45 } | |
46 | |
47 #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR | |
48 class DefaultValueMapAllocator : public ValueMapAllocator | |
49 { | |
50 public: // overridden from ValueMapAllocator | |
51 virtual ValueInternalMap *newMap() | |
52 { | |
53 return new ValueInternalMap(); | |
54 } | |
55 | |
56 virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) | |
57 { | |
58 return new ValueInternalMap( other ); | |
59 } | |
60 | |
61 virtual void destructMap( ValueInternalMap *map ) | |
62 { | |
63 delete map; | |
64 } | |
65 | |
66 virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) | |
67 { | |
68 return new ValueInternalLink[size]; | |
69 } | |
70 | |
71 virtual void releaseMapBuckets( ValueInternalLink *links ) | |
72 { | |
73 delete [] links; | |
74 } | |
75 | |
76 virtual ValueInternalLink *allocateMapLink() | |
77 { | |
78 return new ValueInternalLink(); | |
79 } | |
80 | |
81 virtual void releaseMapLink( ValueInternalLink *link ) | |
82 { | |
83 delete link; | |
84 } | |
85 }; | |
86 #else | |
87 /// @todo make this thread-safe (lock when accessign batch allocator) | |
88 class DefaultValueMapAllocator : public ValueMapAllocator | |
89 { | |
90 public: // overridden from ValueMapAllocator | |
91 virtual ValueInternalMap *newMap() | |
92 { | |
93 ValueInternalMap *map = mapsAllocator_.allocate(); | |
94 new (map) ValueInternalMap(); // placement new | |
95 return map; | |
96 } | |
97 | |
98 virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ) | |
99 { | |
100 ValueInternalMap *map = mapsAllocator_.allocate(); | |
101 new (map) ValueInternalMap( other ); // placement new | |
102 return map; | |
103 } | |
104 | |
105 virtual void destructMap( ValueInternalMap *map ) | |
106 { | |
107 if ( map ) | |
108 { | |
109 map->~ValueInternalMap(); | |
110 mapsAllocator_.release( map ); | |
111 } | |
112 } | |
113 | |
114 virtual ValueInternalLink *allocateMapBuckets( unsigned int size ) | |
115 { | |
116 return new ValueInternalLink[size]; | |
117 } | |
118 | |
119 virtual void releaseMapBuckets( ValueInternalLink *links ) | |
120 { | |
121 delete [] links; | |
122 } | |
123 | |
124 virtual ValueInternalLink *allocateMapLink() | |
125 { | |
126 ValueInternalLink *link = linksAllocator_.allocate(); | |
127 memset( link, 0, sizeof(ValueInternalLink) ); | |
128 return link; | |
129 } | |
130 | |
131 virtual void releaseMapLink( ValueInternalLink *link ) | |
132 { | |
133 link->~ValueInternalLink(); | |
134 linksAllocator_.release( link ); | |
135 } | |
136 private: | |
137 BatchAllocator<ValueInternalMap,1> mapsAllocator_; | |
138 BatchAllocator<ValueInternalLink,1> linksAllocator_; | |
139 }; | |
140 #endif | |
141 | |
142 static ValueMapAllocator *&mapAllocator() | |
143 { | |
144 static DefaultValueMapAllocator defaultAllocator; | |
145 static ValueMapAllocator *mapAllocator = &defaultAllocator; | |
146 return mapAllocator; | |
147 } | |
148 | |
149 static struct DummyMapAllocatorInitializer { | |
150 DummyMapAllocatorInitializer() | |
151 { | |
152 mapAllocator(); // ensure mapAllocator() statics are initialized before main(). | |
153 } | |
154 } dummyMapAllocatorInitializer; | |
155 | |
156 | |
157 | |
158 // h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32. | |
159 | |
160 /* | |
161 use linked list hash map. | |
162 buckets array is a container. | |
163 linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124) | |
164 value have extra state: valid, available, deleted | |
165 */ | |
166 | |
167 | |
168 ValueInternalMap::ValueInternalMap() | |
169 : buckets_( 0 ) | |
170 , tailLink_( 0 ) | |
171 , bucketsSize_( 0 ) | |
172 , itemCount_( 0 ) | |
173 { | |
174 } | |
175 | |
176 | |
177 ValueInternalMap::ValueInternalMap( const ValueInternalMap &other ) | |
178 : buckets_( 0 ) | |
179 , tailLink_( 0 ) | |
180 , bucketsSize_( 0 ) | |
181 , itemCount_( 0 ) | |
182 { | |
183 reserve( other.itemCount_ ); | |
184 IteratorState it; | |
185 IteratorState itEnd; | |
186 other.makeBeginIterator( it ); | |
187 other.makeEndIterator( itEnd ); | |
188 for ( ; !equals(it,itEnd); increment(it) ) | |
189 { | |
190 bool isStatic; | |
191 const char *memberName = key( it, isStatic ); | |
192 const Value &aValue = value( it ); | |
193 resolveReference(memberName, isStatic) = aValue; | |
194 } | |
195 } | |
196 | |
197 | |
198 ValueInternalMap & | |
199 ValueInternalMap::operator =( const ValueInternalMap &other ) | |
200 { | |
201 ValueInternalMap dummy( other ); | |
202 swap( dummy ); | |
203 return *this; | |
204 } | |
205 | |
206 | |
207 ValueInternalMap::~ValueInternalMap() | |
208 { | |
209 if ( buckets_ ) | |
210 { | |
211 for ( BucketIndex bucketIndex =0; bucketIndex < bucketsSize_; ++bucketIndex ) | |
212 { | |
213 ValueInternalLink *link = buckets_[bucketIndex].next_; | |
214 while ( link ) | |
215 { | |
216 ValueInternalLink *linkToRelease = link; | |
217 link = link->next_; | |
218 mapAllocator()->releaseMapLink( linkToRelease ); | |
219 } | |
220 } | |
221 mapAllocator()->releaseMapBuckets( buckets_ ); | |
222 } | |
223 } | |
224 | |
225 | |
226 void | |
227 ValueInternalMap::swap( ValueInternalMap &other ) | |
228 { | |
229 ValueInternalLink *tempBuckets = buckets_; | |
230 buckets_ = other.buckets_; | |
231 other.buckets_ = tempBuckets; | |
232 ValueInternalLink *tempTailLink = tailLink_; | |
233 tailLink_ = other.tailLink_; | |
234 other.tailLink_ = tempTailLink; | |
235 BucketIndex tempBucketsSize = bucketsSize_; | |
236 bucketsSize_ = other.bucketsSize_; | |
237 other.bucketsSize_ = tempBucketsSize; | |
238 BucketIndex tempItemCount = itemCount_; | |
239 itemCount_ = other.itemCount_; | |
240 other.itemCount_ = tempItemCount; | |
241 } | |
242 | |
243 | |
244 void | |
245 ValueInternalMap::clear() | |
246 { | |
247 ValueInternalMap dummy; | |
248 swap( dummy ); | |
249 } | |
250 | |
251 | |
252 ValueInternalMap::BucketIndex | |
253 ValueInternalMap::size() const | |
254 { | |
255 return itemCount_; | |
256 } | |
257 | |
258 bool | |
259 ValueInternalMap::reserveDelta( BucketIndex growth ) | |
260 { | |
261 return reserve( itemCount_ + growth ); | |
262 } | |
263 | |
264 bool | |
265 ValueInternalMap::reserve( BucketIndex newItemCount ) | |
266 { | |
267 if ( !buckets_ && newItemCount > 0 ) | |
268 { | |
269 buckets_ = mapAllocator()->allocateMapBuckets( 1 ); | |
270 bucketsSize_ = 1; | |
271 tailLink_ = &buckets_[0]; | |
272 } | |
273 // BucketIndex idealBucketCount = (newItemCount + ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink; | |
274 return true; | |
275 } | |
276 | |
277 | |
278 const Value * | |
279 ValueInternalMap::find( const char *key ) const | |
280 { | |
281 if ( !bucketsSize_ ) | |
282 return 0; | |
283 HashKey hashedKey = hash( key ); | |
284 BucketIndex bucketIndex = hashedKey % bucketsSize_; | |
285 for ( const ValueInternalLink *current = &buckets_[bucketIndex]; | |
286 current != 0; | |
287 current = current->next_ ) | |
288 { | |
289 for ( BucketIndex index=0; index < ValueInternalLink::itemPerLink; ++index ) | |
290 { | |
291 if ( current->items_[index].isItemAvailable() ) | |
292 return 0; | |
293 if ( strcmp( key, current->keys_[index] ) == 0 ) | |
294 return ¤t->items_[index]; | |
295 } | |
296 } | |
297 return 0; | |
298 } | |
299 | |
300 | |
301 Value * | |
302 ValueInternalMap::find( const char *key ) | |
303 { | |
304 const ValueInternalMap *constThis = this; | |
305 return const_cast<Value *>( constThis->find( key ) ); | |
306 } | |
307 | |
308 | |
309 Value & | |
310 ValueInternalMap::resolveReference( const char *key, | |
311 bool isStatic ) | |
312 { | |
313 HashKey hashedKey = hash( key ); | |
314 if ( bucketsSize_ ) | |
315 { | |
316 BucketIndex bucketIndex = hashedKey % bucketsSize_; | |
317 ValueInternalLink **previous = 0; | |
318 BucketIndex index; | |
319 for ( ValueInternalLink *current = &buckets_[bucketIndex]; | |
320 current != 0; | |
321 previous = ¤t->next_, current = current->next_ ) | |
322 { | |
323 for ( index=0; index < ValueInternalLink::itemPerLink; ++index ) | |
324 { | |
325 if ( current->items_[index].isItemAvailable() ) | |
326 return setNewItem( key, isStatic, current, index ); | |
327 if ( strcmp( key, current->keys_[index] ) == 0 ) | |
328 return current->items_[index]; | |
329 } | |
330 } | |
331 } | |
332 | |
333 reserveDelta( 1 ); | |
334 return unsafeAdd( key, isStatic, hashedKey ); | |
335 } | |
336 | |
337 | |
338 void | |
339 ValueInternalMap::remove( const char *key ) | |
340 { | |
341 HashKey hashedKey = hash( key ); | |
342 if ( !bucketsSize_ ) | |
343 return; | |
344 BucketIndex bucketIndex = hashedKey % bucketsSize_; | |
345 for ( ValueInternalLink *link = &buckets_[bucketIndex]; | |
346 link != 0; | |
347 link = link->next_ ) | |
348 { | |
349 BucketIndex index; | |
350 for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) | |
351 { | |
352 if ( link->items_[index].isItemAvailable() ) | |
353 return; | |
354 if ( strcmp( key, link->keys_[index] ) == 0 ) | |
355 { | |
356 doActualRemove( link, index, bucketIndex ); | |
357 return; | |
358 } | |
359 } | |
360 } | |
361 } | |
362 | |
363 void | |
364 ValueInternalMap::doActualRemove( ValueInternalLink *link, | |
365 BucketIndex index, | |
366 BucketIndex bucketIndex ) | |
367 { | |
368 // find last item of the bucket and swap it with the 'removed' one. | |
369 // set removed items flags to 'available'. | |
370 // if last page only contains 'available' items, then desallocate it (it's empty) | |
371 ValueInternalLink *&lastLink = getLastLinkInBucket( index ); | |
372 BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1 | |
373 for ( ; | |
374 lastItemIndex < ValueInternalLink::itemPerLink; | |
375 ++lastItemIndex ) // may be optimized with dicotomic search | |
376 { | |
377 if ( lastLink->items_[lastItemIndex].isItemAvailable() ) | |
378 break; | |
379 } | |
380 | |
381 BucketIndex lastUsedIndex = lastItemIndex - 1; | |
382 Value *valueToDelete = &link->items_[index]; | |
383 Value *valueToPreserve = &lastLink->items_[lastUsedIndex]; | |
384 if ( valueToDelete != valueToPreserve ) | |
385 valueToDelete->swap( *valueToPreserve ); | |
386 if ( lastUsedIndex == 0 ) // page is now empty | |
387 { // remove it from bucket linked list and delete it. | |
388 ValueInternalLink *linkPreviousToLast = lastLink->previous_; | |
389 if ( linkPreviousToLast != 0 ) // can not deleted bucket link. | |
390 { | |
391 mapAllocator()->releaseMapLink( lastLink ); | |
392 linkPreviousToLast->next_ = 0; | |
393 lastLink = linkPreviousToLast; | |
394 } | |
395 } | |
396 else | |
397 { | |
398 Value dummy; | |
399 valueToPreserve->swap( dummy ); // restore deleted to default Value. | |
400 valueToPreserve->setItemUsed( false ); | |
401 } | |
402 --itemCount_; | |
403 } | |
404 | |
405 | |
406 ValueInternalLink *& | |
407 ValueInternalMap::getLastLinkInBucket( BucketIndex bucketIndex ) | |
408 { | |
409 if ( bucketIndex == bucketsSize_ - 1 ) | |
410 return tailLink_; | |
411 ValueInternalLink *&previous = buckets_[bucketIndex+1].previous_; | |
412 if ( !previous ) | |
413 previous = &buckets_[bucketIndex]; | |
414 return previous; | |
415 } | |
416 | |
417 | |
418 Value & | |
419 ValueInternalMap::setNewItem( const char *key, | |
420 bool isStatic, | |
421 ValueInternalLink *link, | |
422 BucketIndex index ) | |
423 { | |
424 char *duplicatedKey = makeMemberName( key ); | |
425 ++itemCount_; | |
426 link->keys_[index] = duplicatedKey; | |
427 link->items_[index].setItemUsed(); | |
428 link->items_[index].setMemberNameIsStatic( isStatic ); | |
429 return link->items_[index]; // items already default constructed. | |
430 } | |
431 | |
432 | |
433 Value & | |
434 ValueInternalMap::unsafeAdd( const char *key, | |
435 bool isStatic, | |
436 HashKey hashedKey ) | |
437 { | |
438 JSON_ASSERT_MESSAGE( bucketsSize_ > 0, "ValueInternalMap::unsafeAdd(): internal logic error." ); | |
439 BucketIndex bucketIndex = hashedKey % bucketsSize_; | |
440 ValueInternalLink *&previousLink = getLastLinkInBucket( bucketIndex ); | |
441 ValueInternalLink *link = previousLink; | |
442 BucketIndex index; | |
443 for ( index =0; index < ValueInternalLink::itemPerLink; ++index ) | |
444 { | |
445 if ( link->items_[index].isItemAvailable() ) | |
446 break; | |
447 } | |
448 if ( index == ValueInternalLink::itemPerLink ) // need to add a new page | |
449 { | |
450 ValueInternalLink *newLink = mapAllocator()->allocateMapLink(); | |
451 index = 0; | |
452 link->next_ = newLink; | |
453 previousLink = newLink; | |
454 link = newLink; | |
455 } | |
456 return setNewItem( key, isStatic, link, index ); | |
457 } | |
458 | |
459 | |
460 ValueInternalMap::HashKey | |
461 ValueInternalMap::hash( const char *key ) const | |
462 { | |
463 HashKey hash = 0; | |
464 while ( *key ) | |
465 hash += *key++ * 37; | |
466 return hash; | |
467 } | |
468 | |
469 | |
470 int | |
471 ValueInternalMap::compare( const ValueInternalMap &other ) const | |
472 { | |
473 int sizeDiff( itemCount_ - other.itemCount_ ); | |
474 if ( sizeDiff != 0 ) | |
475 return sizeDiff; | |
476 // Strict order guaranty is required. Compare all keys FIRST, then compare values. | |
477 IteratorState it; | |
478 IteratorState itEnd; | |
479 makeBeginIterator( it ); | |
480 makeEndIterator( itEnd ); | |
481 for ( ; !equals(it,itEnd); increment(it) ) | |
482 { | |
483 if ( !other.find( key( it ) ) ) | |
484 return 1; | |
485 } | |
486 | |
487 // All keys are equals, let's compare values | |
488 makeBeginIterator( it ); | |
489 for ( ; !equals(it,itEnd); increment(it) ) | |
490 { | |
491 const Value *otherValue = other.find( key( it ) ); | |
492 int valueDiff = value(it).compare( *otherValue ); | |
493 if ( valueDiff != 0 ) | |
494 return valueDiff; | |
495 } | |
496 return 0; | |
497 } | |
498 | |
499 | |
500 void | |
501 ValueInternalMap::makeBeginIterator( IteratorState &it ) const | |
502 { | |
503 it.map_ = const_cast<ValueInternalMap *>( this ); | |
504 it.bucketIndex_ = 0; | |
505 it.itemIndex_ = 0; | |
506 it.link_ = buckets_; | |
507 } | |
508 | |
509 | |
510 void | |
511 ValueInternalMap::makeEndIterator( IteratorState &it ) const | |
512 { | |
513 it.map_ = const_cast<ValueInternalMap *>( this ); | |
514 it.bucketIndex_ = bucketsSize_; | |
515 it.itemIndex_ = 0; | |
516 it.link_ = 0; | |
517 } | |
518 | |
519 | |
520 bool | |
521 ValueInternalMap::equals( const IteratorState &x, const IteratorState &other ) | |
522 { | |
523 return x.map_ == other.map_ | |
524 && x.bucketIndex_ == other.bucketIndex_ | |
525 && x.link_ == other.link_ | |
526 && x.itemIndex_ == other.itemIndex_; | |
527 } | |
528 | |
529 | |
530 void | |
531 ValueInternalMap::incrementBucket( IteratorState &iterator ) | |
532 { | |
533 ++iterator.bucketIndex_; | |
534 JSON_ASSERT_MESSAGE( iterator.bucketIndex_ <= iterator.map_->bucketsSize_, | |
535 "ValueInternalMap::increment(): attempting to iterate beyond end." ); | |
536 if ( iterator.bucketIndex_ == iterator.map_->bucketsSize_ ) | |
537 iterator.link_ = 0; | |
538 else | |
539 iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]); | |
540 iterator.itemIndex_ = 0; | |
541 } | |
542 | |
543 | |
544 void | |
545 ValueInternalMap::increment( IteratorState &iterator ) | |
546 { | |
547 JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterator using invalid iterator." ); | |
548 ++iterator.itemIndex_; | |
549 if ( iterator.itemIndex_ == ValueInternalLink::itemPerLink ) | |
550 { | |
551 JSON_ASSERT_MESSAGE( iterator.link_ != 0, | |
552 "ValueInternalMap::increment(): attempting to iterate beyond end." ); | |
553 iterator.link_ = iterator.link_->next_; | |
554 if ( iterator.link_ == 0 ) | |
555 incrementBucket( iterator ); | |
556 } | |
557 else if ( iterator.link_->items_[iterator.itemIndex_].isItemAvailable() ) | |
558 { | |
559 incrementBucket( iterator ); | |
560 } | |
561 } | |
562 | |
563 | |
564 void | |
565 ValueInternalMap::decrement( IteratorState &iterator ) | |
566 { | |
567 if ( iterator.itemIndex_ == 0 ) | |
568 { | |
569 JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterate using invalid iterator." ); | |
570 if ( iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_] ) | |
571 { | |
572 JSON_ASSERT_MESSAGE( iterator.bucketIndex_ > 0, "Attempting to iterate beyond beginning." ); | |
573 --(iterator.bucketIndex_); | |
574 } | |
575 iterator.link_ = iterator.link_->previous_; | |
576 iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1; | |
577 } | |
578 } | |
579 | |
580 | |
581 const char * | |
582 ValueInternalMap::key( const IteratorState &iterator ) | |
583 { | |
584 JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); | |
585 return iterator.link_->keys_[iterator.itemIndex_]; | |
586 } | |
587 | |
588 const char * | |
589 ValueInternalMap::key( const IteratorState &iterator, bool &isStatic ) | |
590 { | |
591 JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); | |
592 isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic(); | |
593 return iterator.link_->keys_[iterator.itemIndex_]; | |
594 } | |
595 | |
596 | |
597 Value & | |
598 ValueInternalMap::value( const IteratorState &iterator ) | |
599 { | |
600 JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." ); | |
601 return iterator.link_->items_[iterator.itemIndex_]; | |
602 } | |
603 | |
604 | |
605 int | |
606 ValueInternalMap::distance( const IteratorState &x, const IteratorState &y ) | |
607 { | |
608 int offset = 0; | |
609 IteratorState it = x; | |
610 while ( !equals( it, y ) ) | |
611 increment( it ); | |
612 return offset; | |
613 } | |
614 | |
615 } // namespace Json |