summaryrefslogtreecommitdiff
path: root/plugins/JSON/Source/JSONChildren.h
blob: d7a38bb9dc2b36f738f003248f2d2201e60fd880 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#ifndef JSONCHILDREN_H
#define JSONCHILDREN_H

#include "JSONMemory.h"
#include "JSONDebug.h"  //for JSON_ASSERT macro

#define json_foreach(children, iterator)\
    JSONNode ** iterator = children.begin();\
    for(JSONNode ** iterator##_end = children.end(); iterator != iterator##_end; ++iterator)

/*
 This class is essentially a vector that has been heavily optimized for the specific purpose
 of holding JSONNode children.  It acts the same way as a vector, it has a automatically
 expanding array.  On destruction, this container automatically destroys everything contained
 in it as well, so that you libJSON doesn't have to do that.
 
 T is JSONNode*, I can't define it that way directly because JSONNode uses this container, and because
 the container deletes the children automatically, forward declaration can't be used
 */

class JSONNode;  //forward declaration

class jsonChildren {
public:
    //starts completely empty and the array is not allocated
    jsonChildren(void) : array(0), mysize(0), mycapacity(0) { }
    
    //deletes the array and everything that is contained within it (using delete)
    ~jsonChildren(void){
	   if (array){  //the following function calls are safe, but take more time than a check here
		  deleteAll();
		  libjson_free<JSONNode*>(array);
	   }
    }
    
    //increase the size of the array
    void inc(json_index_t amount);
    void inc(void);
    
    //Adds something to the vector, doubling the array if necessary
    void push_back(JSONNode * item){
	   inc();
	   array[mysize++] = item;
    }
    
    //Adds something to the front of the vector, doubling the array if necessary
    void push_front(JSONNode * item){
	   inc();
	   memmove(array + 1, array, mysize++ * sizeof(JSONNode *));
	   array[0] = item;
    }
    
    //gets an item out of the vector by it's position
    inline JSONNode * operator[] (json_index_t position) const {
	   JSON_ASSERT(position < mysize, JSON_TEXT("Using [] out of bounds"));
	   JSON_ASSERT(position < mycapacity, JSON_TEXT("Using [] out of bounds"));
	   JSON_ASSERT(array, JSON_TEXT("Array is null"));
	   return array[position];
    }
    
    //returns the allocated capacity, but keep in mind that some might not be valid
    inline json_index_t capacity() const {
	   return mycapacity;
    }
    
    //returns the number of valid objects within the vector
    inline json_index_t size() const {
	   return mysize;
    }
    
    //tests whether or not the vector is empty
    inline bool empty() const {
	   return mysize == 0;
    }
    
    //clears (and deletes) everything from the vector and sets it's size to 0
    inline void clear() {
	   if (array){  //don't bother clearing anything if there is nothing in it
		  JSON_ASSERT(mycapacity != 0, JSON_TEXT("mycapacity is not zero, but array is null"));
		  deleteAll();
		  mysize = 0;
	   }
	   JSON_ASSERT(mysize == 0, JSON_TEXT("mysize is not zero after clear"));
    }
    
    //returns the beginning of the array
    inline JSONNode ** begin(void) const {
	   return array;
    }
    
    //returns the end of the array
    inline JSONNode ** end(void) const {
	   return array + mysize;
    }
    
    //makes sure that even after shirnking and expanding, the iterator is in same relative position
    struct iteratorKeeper {
    public:
	   #ifdef JSON_LIBRARY
		  iteratorKeeper(jsonChildren * pthis, JSONNode ** & position) : 
			 myRelativeOffset((json_index_t)(position - pthis -> array)),
	   #else
		  iteratorKeeper(jsonChildren * pthis, JSONNode ** & position, bool reverse = false) : 
			 myRelativeOffset(reverse ? (json_index_t)(pthis -> array + (size_t)pthis -> mysize - position) : (json_index_t)(position - pthis -> array)),
			 myReverse(reverse),
	   #endif
			 myChildren(pthis), 
			 myPos(position){}

	   ~iteratorKeeper(void){
		  #ifdef JSON_LIBRARY
			 myPos = myChildren -> array + myRelativeOffset;
		  #else
			 if (myReverse){
				myPos = myChildren -> array + myChildren -> mysize - myRelativeOffset;
			 } else {
				myPos = myChildren -> array + myRelativeOffset;
			 }
		  #endif
	   }
    private:
	   iteratorKeeper(const iteratorKeeper &);
	   iteratorKeeper & operator = (const iteratorKeeper &);
	   
	   jsonChildren * myChildren;
	   JSONNode ** & myPos;
	   json_index_t myRelativeOffset;
	   #ifndef JSON_LIBRARY
		  bool myReverse BITS(1);
	   #endif
    };
    
    //This function DOES NOT delete the item it points to
    inline void erase(JSONNode ** & position){
	   JSON_ASSERT(array, JSON_TEXT("erasing something from a null array 1"));
	   JSON_ASSERT(position >= array, JSON_TEXT("position is beneath the start of the array 1"));
	   JSON_ASSERT(position <= array + mysize, JSON_TEXT("erasing out of bounds 1"));
	   memmove(position, position + 1, (mysize-- - (position - array) - 1) * sizeof(JSONNode *));
	   iteratorKeeper ik(this, position);
	   shrink();
    }
    
    //This function DOES NOT delete the item it points to
    inline void erase(JSONNode ** & position, json_index_t number){
	   doerase(position, number);
	   iteratorKeeper ik(this, position);
	   shrink();
    }
    
    
    //This function DOES NOT delete the item it points to
    inline void erase(JSONNode ** position, json_index_t number, JSONNode ** & starter){
	   doerase(position, number);
	   iteratorKeeper ik(this, starter);
	   shrink();
    }
    
    #ifdef JSON_LIBRARY
	   void insert(JSONNode ** & position, JSONNode * item){
    #else
	   void insert(JSONNode ** & position, JSONNode * item, bool reverse = false){
    #endif
	   //position isnt relative to array because of realloc
	   JSON_ASSERT(position >= array, JSON_TEXT("position is beneath the start of the array insert 1"));
	   JSON_ASSERT(position <= array + mysize, JSON_TEXT("position is above the end of the array insert 1"));
	   {
		  #ifdef JSON_LIBRARY
			 iteratorKeeper ik(this, position);
		  #else
			 iteratorKeeper ik(this, position, reverse);
		  #endif
		  inc();
	   }
	   memmove(position + 1, position, (mysize++ - (position - array)) * sizeof(JSONNode *));
	   *position = item;
    }

    void insert(JSONNode ** & position, JSONNode ** items, json_index_t num){
	   JSON_ASSERT(position >= array, JSON_TEXT("position is beneath the start of the array insert 2"));
	   JSON_ASSERT(position <= array + mysize, JSON_TEXT("position is above the end of the array insert 2"));
	   {
		  iteratorKeeper ik(this, position);
		  inc(num);
	   }
	   const size_t ptrs = ((JSONNode **)(array + mysize)) - position;
	   memmove(position + num, position, ptrs * sizeof(JSONNode *));
	   memcpy(position, items, num * sizeof(JSONNode *));
	   mysize += num;
    }
    
    inline void reserve(json_index_t amount){
	   JSON_ASSERT(!array, JSON_TEXT("reserve is not meant to expand a preexisting array"));
	   JSON_ASSERT(!mycapacity, JSON_TEXT("reservec is not meant to expand a preexisting array"));
	   JSON_ASSERT(!mysize, JSON_TEXT("reserves is not meant to expand a preexisting array"));
	   array = json_malloc<JSONNode*>(mycapacity = amount);
    }

    inline void reserve2(json_index_t amount){
	   if (array){
		  if (mycapacity < amount) inc(amount - mycapacity);
	   } else {
		  reserve(amount);
	   }
    }
		  
    //shrinks the array to only as large as it needs to be to hold everything within it
    inline void shrink() {
	   if (mysize == 0){  //size is zero, we should completely free the array
		  libjson_free<JSONNode*>(array);  //free does checks for a null pointer, so don't bother checking
		  array = 0;
		  #ifdef JSON_LESS_MEMORY
			 } else {  //need to shrink it, using realloc
				JSON_ASSERT(array, JSON_TEXT("shrinking a null array that is not size 0"));
				array = json_realloc<JSONNode*>(array, mysize);
		  #endif  
	   }
	   mycapacity = mysize;
    }
JSON_PRIVATE
    //to make sure it's not copyable
    jsonChildren(const jsonChildren &);
    jsonChildren & operator = (const jsonChildren &);
    
    void deleteAll(void);  //implemented in JSONNode.cpp
    void doerase(JSONNode ** position, json_index_t number);
    
    JSONNode ** array;  //the expandable array

    json_index_t mysize;	     //the number of valid items
    json_index_t mycapacity;   //the number of possible items
};


#endif