Leon Scroggins III | f59fb0e | 2014-05-28 15:19:42 -0400 | [diff] [blame^] | 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 |