JsonCpp project page JsonCpp home page

json_internalmap.inl
Go to the documentation of this file.
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 
22 ValueInternalLink::ValueInternalLink() : previous_(0), next_(0) {}
23 
25  for (int index = 0; index < itemPerLink; ++index) {
26  if (!items_[index].isItemAvailable()) {
27  if (!items_[index].isMemberNameStatic())
28  free(keys_[index]);
29  } else
30  break;
31  }
32 }
33 
35 
36 #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
37 class DefaultValueMapAllocator : public ValueMapAllocator {
38 public: // overridden from ValueMapAllocator
39  virtual ValueInternalMap* newMap() { return new ValueInternalMap(); }
40 
41  virtual ValueInternalMap* newMapCopy(const ValueInternalMap& other) {
42  return new ValueInternalMap(other);
43  }
44 
45  virtual void destructMap(ValueInternalMap* map) { delete map; }
46 
47  virtual ValueInternalLink* allocateMapBuckets(unsigned int size) {
48  return new ValueInternalLink[size];
49  }
50 
51  virtual void releaseMapBuckets(ValueInternalLink* links) { delete[] links; }
52 
53  virtual ValueInternalLink* allocateMapLink() {
54  return new ValueInternalLink();
55  }
56 
57  virtual void releaseMapLink(ValueInternalLink* link) { delete link; }
58 };
59 #else
60 class DefaultValueMapAllocator : public ValueMapAllocator {
62 public: // overridden from ValueMapAllocator
63  virtual ValueInternalMap* newMap() {
64  ValueInternalMap* map = mapsAllocator_.allocate();
65  new (map) ValueInternalMap(); // placement new
66  return map;
67  }
68 
69  virtual ValueInternalMap* newMapCopy(const ValueInternalMap& other) {
70  ValueInternalMap* map = mapsAllocator_.allocate();
71  new (map) ValueInternalMap(other); // placement new
72  return map;
73  }
74 
75  virtual void destructMap(ValueInternalMap* map) {
76  if (map) {
77  map->~ValueInternalMap();
78  mapsAllocator_.release(map);
79  }
80  }
81 
82  virtual ValueInternalLink* allocateMapBuckets(unsigned int size) {
83  return new ValueInternalLink[size];
84  }
85 
86  virtual void releaseMapBuckets(ValueInternalLink* links) { delete[] links; }
87 
88  virtual ValueInternalLink* allocateMapLink() {
89  ValueInternalLink* link = linksAllocator_.allocate();
90  memset(link, 0, sizeof(ValueInternalLink));
91  return link;
92  }
93 
94  virtual void releaseMapLink(ValueInternalLink* link) {
95  link->~ValueInternalLink();
96  linksAllocator_.release(link);
97  }
98 
99 private:
100  BatchAllocator<ValueInternalMap, 1> mapsAllocator_;
101  BatchAllocator<ValueInternalLink, 1> linksAllocator_;
102 };
103 #endif
104 
106  static DefaultValueMapAllocator defaultAllocator;
107  static ValueMapAllocator* mapAllocator = &defaultAllocator;
108  return mapAllocator;
109 }
110 
111 static struct DummyMapAllocatorInitializer {
112  DummyMapAllocatorInitializer() {
113  mapAllocator(); // ensure mapAllocator() statics are initialized before
114  // main().
115  }
117 
118 // h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32.
119 
120 /*
121 use linked list hash map.
122 buckets array is a container.
123 linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124)
124 value have extra state: valid, available, deleted
125 */
126 
128  : buckets_(0), tailLink_(0), bucketsSize_(0), itemCount_(0) {}
129 
131  : buckets_(0), tailLink_(0), bucketsSize_(0), itemCount_(0) {
132  reserve(other.itemCount_);
133  IteratorState it;
134  IteratorState itEnd;
135  other.makeBeginIterator(it);
136  other.makeEndIterator(itEnd);
137  for (; !equals(it, itEnd); increment(it)) {
138  bool isStatic;
139  const char* memberName = key(it, isStatic);
140  const Value& aValue = value(it);
141  resolveReference(memberName, isStatic) = aValue;
142  }
143 }
144 
146  swap(other);
147  return *this;
148 }
149 
151  if (buckets_) {
152  for (BucketIndex bucketIndex = 0; bucketIndex < bucketsSize_;
153  ++bucketIndex) {
154  ValueInternalLink* link = buckets_[bucketIndex].next_;
155  while (link) {
156  ValueInternalLink* linkToRelease = link;
157  link = link->next_;
158  mapAllocator()->releaseMapLink(linkToRelease);
159  }
160  }
161  mapAllocator()->releaseMapBuckets(buckets_);
162  }
163 }
164 
166  ValueInternalLink* tempBuckets = buckets_;
167  buckets_ = other.buckets_;
168  other.buckets_ = tempBuckets;
169  ValueInternalLink* tempTailLink = tailLink_;
170  tailLink_ = other.tailLink_;
171  other.tailLink_ = tempTailLink;
172  BucketIndex tempBucketsSize = bucketsSize_;
173  bucketsSize_ = other.bucketsSize_;
174  other.bucketsSize_ = tempBucketsSize;
175  BucketIndex tempItemCount = itemCount_;
176  itemCount_ = other.itemCount_;
177  other.itemCount_ = tempItemCount;
178 }
179 
181  ValueInternalMap dummy;
182  swap(dummy);
183 }
184 
186  return itemCount_;
187 }
188 
190  return reserve(itemCount_ + growth);
191 }
192 
194  if (!buckets_ && newItemCount > 0) {
195  buckets_ = mapAllocator()->allocateMapBuckets(1);
196  bucketsSize_ = 1;
197  tailLink_ = &buckets_[0];
198  }
199  // BucketIndex idealBucketCount = (newItemCount +
200  // ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink;
201  return true;
202 }
203 
204 const Value* ValueInternalMap::find(const char* key) const {
205  if (!bucketsSize_)
206  return 0;
207  HashKey hashedKey = hash(key);
208  BucketIndex bucketIndex = hashedKey % bucketsSize_;
209  for (const ValueInternalLink* current = &buckets_[bucketIndex]; current != 0;
210  current = current->next_) {
211  for (BucketIndex index = 0; index < ValueInternalLink::itemPerLink;
212  ++index) {
213  if (current->items_[index].isItemAvailable())
214  return 0;
215  if (strcmp(key, current->keys_[index]) == 0)
216  return &current->items_[index];
217  }
218  }
219  return 0;
220 }
221 
222 Value* ValueInternalMap::find(const char* key) {
223  const ValueInternalMap* constThis = this;
224  return const_cast<Value*>(constThis->find(key));
225 }
226 
227 Value& ValueInternalMap::resolveReference(const char* key, bool isStatic) {
228  HashKey hashedKey = hash(key);
229  if (bucketsSize_) {
230  BucketIndex bucketIndex = hashedKey % bucketsSize_;
231  ValueInternalLink** previous = 0;
232  BucketIndex index;
233  for (ValueInternalLink* current = &buckets_[bucketIndex]; current != 0;
234  previous = &current->next_, current = current->next_) {
235  for (index = 0; index < ValueInternalLink::itemPerLink; ++index) {
236  if (current->items_[index].isItemAvailable())
237  return setNewItem(key, isStatic, current, index);
238  if (strcmp(key, current->keys_[index]) == 0)
239  return current->items_[index];
240  }
241  }
242  }
243 
244  reserveDelta(1);
245  return unsafeAdd(key, isStatic, hashedKey);
246 }
247 
248 void ValueInternalMap::remove(const char* key) {
249  HashKey hashedKey = hash(key);
250  if (!bucketsSize_)
251  return;
252  BucketIndex bucketIndex = hashedKey % bucketsSize_;
253  for (ValueInternalLink* link = &buckets_[bucketIndex]; link != 0;
254  link = link->next_) {
255  BucketIndex index;
256  for (index = 0; index < ValueInternalLink::itemPerLink; ++index) {
257  if (link->items_[index].isItemAvailable())
258  return;
259  if (strcmp(key, link->keys_[index]) == 0) {
260  doActualRemove(link, index, bucketIndex);
261  return;
262  }
263  }
264  }
265 }
266 
268  BucketIndex index,
269  BucketIndex bucketIndex) {
270  // find last item of the bucket and swap it with the 'removed' one.
271  // set removed items flags to 'available'.
272  // if last page only contains 'available' items, then desallocate it (it's
273  // empty)
274  ValueInternalLink*& lastLink = getLastLinkInBucket(index);
275  BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1
276  for (; lastItemIndex < ValueInternalLink::itemPerLink;
277  ++lastItemIndex) // may be optimized with dicotomic search
278  {
279  if (lastLink->items_[lastItemIndex].isItemAvailable())
280  break;
281  }
282 
283  BucketIndex lastUsedIndex = lastItemIndex - 1;
284  Value* valueToDelete = &link->items_[index];
285  Value* valueToPreserve = &lastLink->items_[lastUsedIndex];
286  if (valueToDelete != valueToPreserve)
287  valueToDelete->swap(*valueToPreserve);
288  if (lastUsedIndex == 0) // page is now empty
289  { // remove it from bucket linked list and delete it.
290  ValueInternalLink* linkPreviousToLast = lastLink->previous_;
291  if (linkPreviousToLast != 0) // can not deleted bucket link.
292  {
293  mapAllocator()->releaseMapLink(lastLink);
294  linkPreviousToLast->next_ = 0;
295  lastLink = linkPreviousToLast;
296  }
297  } else {
298  Value dummy;
299  valueToPreserve->swap(dummy); // restore deleted to default Value.
300  valueToPreserve->setItemUsed(false);
301  }
302  --itemCount_;
303 }
304 
307  if (bucketIndex == bucketsSize_ - 1)
308  return tailLink_;
309  ValueInternalLink*& previous = buckets_[bucketIndex + 1].previous_;
310  if (!previous)
311  previous = &buckets_[bucketIndex];
312  return previous;
313 }
314 
316  bool isStatic,
317  ValueInternalLink* link,
318  BucketIndex index) {
319  char* duplicatedKey = makeMemberName(key);
320  ++itemCount_;
321  link->keys_[index] = duplicatedKey;
322  link->items_[index].setItemUsed();
323  link->items_[index].setMemberNameIsStatic(isStatic);
324  return link->items_[index]; // items already default constructed.
325 }
326 
327 Value&
328 ValueInternalMap::unsafeAdd(const char* key, bool isStatic, HashKey hashedKey) {
329  JSON_ASSERT_MESSAGE(bucketsSize_ > 0,
330  "ValueInternalMap::unsafeAdd(): internal logic error.");
331  BucketIndex bucketIndex = hashedKey % bucketsSize_;
332  ValueInternalLink*& previousLink = getLastLinkInBucket(bucketIndex);
333  ValueInternalLink* link = previousLink;
334  BucketIndex index;
335  for (index = 0; index < ValueInternalLink::itemPerLink; ++index) {
336  if (link->items_[index].isItemAvailable())
337  break;
338  }
339  if (index == ValueInternalLink::itemPerLink) // need to add a new page
340  {
342  index = 0;
343  link->next_ = newLink;
344  previousLink = newLink;
345  link = newLink;
346  }
347  return setNewItem(key, isStatic, link, index);
348 }
349 
351  HashKey hash = 0;
352  while (*key)
353  hash += *key++ * 37;
354  return hash;
355 }
356 
358  int sizeDiff(itemCount_ - other.itemCount_);
359  if (sizeDiff != 0)
360  return sizeDiff;
361  // Strict order guaranty is required. Compare all keys FIRST, then compare
362  // values.
363  IteratorState it;
364  IteratorState itEnd;
365  makeBeginIterator(it);
366  makeEndIterator(itEnd);
367  for (; !equals(it, itEnd); increment(it)) {
368  if (!other.find(key(it)))
369  return 1;
370  }
371 
372  // All keys are equals, let's compare values
373  makeBeginIterator(it);
374  for (; !equals(it, itEnd); increment(it)) {
375  const Value* otherValue = other.find(key(it));
376  int valueDiff = value(it).compare(*otherValue);
377  if (valueDiff != 0)
378  return valueDiff;
379  }
380  return 0;
381 }
382 
383 void ValueInternalMap::makeBeginIterator(IteratorState& it) const {
384  it.map_ = const_cast<ValueInternalMap*>(this);
385  it.bucketIndex_ = 0;
386  it.itemIndex_ = 0;
387  it.link_ = buckets_;
388 }
389 
390 void ValueInternalMap::makeEndIterator(IteratorState& it) const {
391  it.map_ = const_cast<ValueInternalMap*>(this);
392  it.bucketIndex_ = bucketsSize_;
393  it.itemIndex_ = 0;
394  it.link_ = 0;
395 }
396 
397 bool ValueInternalMap::equals(const IteratorState& x,
398  const IteratorState& other) {
399  return x.map_ == other.map_ && x.bucketIndex_ == other.bucketIndex_ &&
400  x.link_ == other.link_ && x.itemIndex_ == other.itemIndex_;
401 }
402 
403 void ValueInternalMap::incrementBucket(IteratorState& iterator) {
404  ++iterator.bucketIndex_;
406  iterator.bucketIndex_ <= iterator.map_->bucketsSize_,
407  "ValueInternalMap::increment(): attempting to iterate beyond end.");
408  if (iterator.bucketIndex_ == iterator.map_->bucketsSize_)
409  iterator.link_ = 0;
410  else
411  iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]);
412  iterator.itemIndex_ = 0;
413 }
414 
415 void ValueInternalMap::increment(IteratorState& iterator) {
416  JSON_ASSERT_MESSAGE(iterator.map_,
417  "Attempting to iterator using invalid iterator.");
418  ++iterator.itemIndex_;
419  if (iterator.itemIndex_ == ValueInternalLink::itemPerLink) {
421  iterator.link_ != 0,
422  "ValueInternalMap::increment(): attempting to iterate beyond end.");
423  iterator.link_ = iterator.link_->next_;
424  if (iterator.link_ == 0)
425  incrementBucket(iterator);
426  } else if (iterator.link_->items_[iterator.itemIndex_].isItemAvailable()) {
427  incrementBucket(iterator);
428  }
429 }
430 
431 void ValueInternalMap::decrement(IteratorState& iterator) {
432  if (iterator.itemIndex_ == 0) {
433  JSON_ASSERT_MESSAGE(iterator.map_,
434  "Attempting to iterate using invalid iterator.");
435  if (iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_]) {
436  JSON_ASSERT_MESSAGE(iterator.bucketIndex_ > 0,
437  "Attempting to iterate beyond beginning.");
438  --(iterator.bucketIndex_);
439  }
440  iterator.link_ = iterator.link_->previous_;
441  iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1;
442  }
443 }
444 
445 const char* ValueInternalMap::key(const IteratorState& iterator) {
446  JSON_ASSERT_MESSAGE(iterator.link_,
447  "Attempting to iterate using invalid iterator.");
448  return iterator.link_->keys_[iterator.itemIndex_];
449 }
450 
451 const char* ValueInternalMap::key(const IteratorState& iterator,
452  bool& isStatic) {
453  JSON_ASSERT_MESSAGE(iterator.link_,
454  "Attempting to iterate using invalid iterator.");
455  isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic();
456  return iterator.link_->keys_[iterator.itemIndex_];
457 }
458 
459 Value& ValueInternalMap::value(const IteratorState& iterator) {
460  JSON_ASSERT_MESSAGE(iterator.link_,
461  "Attempting to iterate using invalid iterator.");
462  return iterator.link_->items_[iterator.itemIndex_];
463 }
464 
465 int ValueInternalMap::distance(const IteratorState& x, const IteratorState& y) {
466  int offset = 0;
467  IteratorState it = x;
468  while (!equals(it, y))
469  increment(it);
470  return offset;
471 }
472 
473 } // namespace Json
unsigned int HashKey
Definition: value.h:669
bool reserveDelta(BucketIndex growth)
#define JSON_ASSERT_MESSAGE(condition, message)
Definition: assertions.h:36
int compare(const Value &other) const
Definition: json_value.cpp:502
void remove(const char *key)
Value & unsafeAdd(const char *key, bool isStatic, HashKey hashedKey)
void swap(ValueInternalMap &other)
virtual void releaseMapLink(ValueInternalLink *link)=0
int compare(const ValueInternalMap &other) const
virtual void releaseMapBuckets(ValueInternalLink *links)=0
void doActualRemove(ValueInternalLink *link, BucketIndex index, BucketIndex bucketIndex)
bool reserve(BucketIndex newItemCount)
Allocator to customize Value internal map.
Definition: value.h:612
Value & setNewItem(const char *key, bool isStatic, ValueInternalLink *link, BucketIndex index)
static struct Json::DummyMapAllocatorInitializer dummyMapAllocatorInitializer
ValueInternalLink *& getLastLinkInBucket(BucketIndex bucketIndex)
const Value * find(const char *key) const
void swap(Value &other)
Swap values.
Definition: json_value.cpp:488
unsigned int BucketIndex
Definition: value.h:670
Value & resolveReference(const char *key, bool isStatic)
static ValueMapAllocator *& mapAllocator()
Represents a JSON value.
Definition: value.h:116
ValueInternalMap & operator=(ValueInternalMap other)
A linked page based hash-table implementation used internally by Value.
Definition: value.h:664
virtual ValueInternalLink * allocateMapLink()=0
HashKey hash(const char *key) const
virtual ValueInternalLink * allocateMapBuckets(unsigned int size)=0
BucketIndex size() const