comparison d2/qt/core/QList.d @ 344:96a75b1e5b26

project structure changes
author Max Samukha <maxter@spambox.com>
date Fri, 14 May 2010 12:14:37 +0300
parents qt/core/QList.d@5896535a03cd
children 970332a88b72
comparison
equal deleted inserted replaced
343:552647ec0f82 344:96a75b1e5b26
1 module qt.core.QList;
2
3 import qt.QGlobal;
4 import qt.core.QTypeInfo;
5 import qt.core.QString;
6 import qtd.QtdObject;
7 import qtd.Atomic;
8 import qtd.MetaMarshall;
9
10 import core.stdc.stdlib : qRealloc = realloc, qFree = free, qMalloc = malloc;
11 import core.stdc.string : memcpy, memmove;
12
13 import std.traits;
14
15 enum INT_MAX = int.max;
16
17 bool isComplex(T)()
18 if (is(T.QTypeInfo))
19 {
20 return T.QTypeInfo.isComplex;
21 }
22
23 bool isStatic(T)()
24 if (is(T.QTypeInfo))
25 {
26 return T.QTypeInfo.isStatic;
27 }
28
29 bool isLarge(T)()
30 if (is(T.QTypeInfo))
31 {
32 return T.QTypeInfo.isLarge;
33 }
34
35 template isQtReference(T)
36 {
37 enum isQtReference = isQObjectType!T || isObjectType!T || isValueType!T || is(T == string);
38 }
39
40 int qAllocMore(int alloc, int extra)
41 {
42 if (alloc == 0 && extra == 0)
43 return 0;
44 const int page = 1 << 12;
45 int nalloc;
46 alloc += extra;
47 if (alloc < 1<<6) {
48 nalloc = (1<<3) + ((alloc >>3) << 3);
49 } else {
50 // don't do anything if the loop will overflow signed int.
51 if (alloc >= INT_MAX/2)
52 return INT_MAX;
53 nalloc = (alloc < page) ? 1 << 3 : page;
54 while (nalloc < alloc) {
55 if (nalloc <= 0)
56 return INT_MAX;
57 nalloc *= 2;
58 }
59 }
60 return nalloc - extra;
61 }
62
63 void q_new_at(T)(T* ptr, const ref T t)
64 {
65 memcpy(ptr, &t, T.sizeof);
66 /* static if (__traits(compiles, ptr.__postblit())) DMD bug #3539
67 ptr.__postblit();*/
68 }
69
70 T* q_new(T)(const ref T t)
71 {
72 T* ptr = cast(T*) qMalloc(T.sizeof);
73 q_new_at!T(ptr, t);
74 return ptr;
75 }
76
77 void q_delete(T)(T* t)
78 {
79 static if (__traits(compiles, t.__dtor()))
80 t.__dtor();
81 qFree(t);
82 }
83
84 private int grow(int size)
85 {
86 // dear compiler: don't optimize me out.
87 // synchronized {
88 int x = qAllocMore(size * (void*).sizeof, QListData.DataHeaderSize) / (void*).sizeof;
89 return x;
90 // }
91 }
92
93 struct QListData {
94 private:
95 struct Data {
96 Atomic!int ref_;
97 int alloc, begin, end;
98 uint sharable;
99 void*[1] array;
100 }
101
102 enum { DataHeaderSize = Data.sizeof - (void*).sizeof }
103
104 static Data shared_null;
105 Data *d;
106
107 static this()
108 {
109 shared_null = Data(Atomic!int(1), 0, 0, 0, true, [null]);
110 }
111
112
113 // Data *detach(); // remove in 5.0
114
115 Data* detach2()
116 {
117 Data* x = d;
118 d = cast(Data*)(qMalloc(DataHeaderSize + x.alloc * (void*).sizeof));
119 if (!d)
120 qFatal("QList: Out of memory");
121
122 memcpy(d, x, DataHeaderSize + x.alloc * (void*).sizeof);
123 d.alloc = x.alloc;
124 d.ref_.store(1);
125 d.sharable = true;
126 if (!d.alloc)
127 d.begin = d.end = 0;
128
129 return x;
130 }
131
132 void realloc(int alloc)
133 {
134 // assert(d.ref_ == 1);
135 Data* x = cast(Data*)(qRealloc(d, DataHeaderSize + alloc * (void*).sizeof));
136 if (!x)
137 qFatal("QList: Out of memory");
138
139 d = x;
140 d.alloc = alloc;
141 if (!alloc)
142 d.begin = d.end = 0;
143 }
144
145 void** append()
146 {
147 // #TODO Q_ASSERT(d.ref_ == 1);
148 if (d.end == d.alloc) {
149 int n = d.end - d.begin;
150 if (d.begin > 2 * d.alloc / 3) {
151 memcpy(d.array.ptr + n, d.array.ptr + d.begin, n * (void*).sizeof);
152 d.begin = n;
153 d.end = n * 2;
154 } else {
155 realloc(grow(d.alloc + 1));
156 }
157 }
158 return d.array.ptr + d.end++;
159 }
160
161 void **append(const ref QListData l)
162 {
163 // Q_ASSERT(d.ref_ == 1);
164 int e = d.end;
165 int n = l.d.end - l.d.begin;
166 if (n) {
167 if (e + n > d.alloc)
168 realloc(grow(e + l.d.end - l.d.begin));
169 memcpy(d.array.ptr + d.end, l.d.array.ptr + l.d.begin, n * (void*).sizeof);
170 d.end += n;
171 }
172 return d.array.ptr + e;
173 }
174
175 void **prepend()
176 {
177 // Q_ASSERT(d.ref_ == 1);
178 if (d.begin == 0) {
179 if (d.end >= d.alloc / 3)
180 realloc(grow(d.alloc + 1));
181
182 if (d.end < d.alloc / 3)
183 d.begin = d.alloc - 2 * d.end;
184 else
185 d.begin = d.alloc - d.end;
186
187 memmove(d.array.ptr + d.begin, d.array.ptr, d.end * (void*).sizeof);
188 d.end += d.begin;
189 }
190 return d.array.ptr + --d.begin;
191 }
192
193 void **insert(int i)
194 {
195 // Q_ASSERT(d.ref_ == 1);
196 if (i <= 0)
197 return prepend();
198 if (i >= d.end - d.begin)
199 return append();
200
201 bool leftward = false;
202 int size = d.end - d.begin;
203
204 if (d.begin == 0) {
205 if (d.end == d.alloc) {
206 // If the array is full, we expand it and move some items rightward
207 realloc(grow(d.alloc + 1));
208 } else {
209 // If there is free space at the end of the array, we move some items rightward
210 }
211 } else {
212 if (d.end == d.alloc) {
213 // If there is free space at the beginning of the array, we move some items leftward
214 leftward = true;
215 } else {
216 // If there is free space at both ends, we move as few items as possible
217 leftward = (i < size - i);
218 }
219 }
220
221 if (leftward) {
222 --d.begin;
223 memmove(d.array.ptr + d.begin, d.array.ptr + d.begin + 1, i * (void*).sizeof);
224 } else {
225 memmove(d.array.ptr + d.begin + i + 1, d.array.ptr + d.begin + i,
226 (size - i) * (void*).sizeof);
227 ++d.end;
228 }
229 return d.array.ptr + d.begin + i;
230 }
231
232 void remove(int i)
233 {
234 // Q_ASSERT(d.ref_ == 1);
235 i += d.begin;
236 if (i - d.begin < d.end - i) {
237 if (int offset = i - d.begin)
238 memmove(d.array.ptr + d.begin + 1, d.array.ptr + d.begin, offset * (void*).sizeof);
239 d.begin++;
240 } else {
241 if (int offset = d.end - i - 1)
242 memmove(d.array.ptr + i, d.array.ptr + i + 1, offset * (void*).sizeof);
243 d.end--;
244 }
245 }
246
247 void remove(int i, int n)
248 {
249 // Q_ASSERT(d.ref_ == 1);
250 i += d.begin;
251 int middle = i + n/2;
252 if (middle - d.begin < d.end - middle) {
253 memmove(d.array.ptr + d.begin + n, d.array.ptr + d.begin,
254 (i - d.begin) * (void*).sizeof);
255 d.begin += n;
256 } else {
257 memmove(d.array.ptr + i, d.array.ptr + i + n,
258 (d.end - i - n) * (void*).sizeof);
259 d.end -= n;
260 }
261 }
262
263 void move(int from, int to)
264 {
265 // Q_ASSERT(d.ref_ == 1);
266 if (from == to)
267 return;
268
269 from += d.begin;
270 to += d.begin;
271 void *t = d.array.ptr[from];
272
273 if (from < to) {
274 if (d.end == d.alloc || 3 * (to - from) < 2 * (d.end - d.begin)) {
275 memmove(d.array.ptr + from, d.array.ptr + from + 1, (to - from) * (void*).sizeof);
276 } else {
277 // optimization
278 if (int offset = from - d.begin)
279 memmove(d.array.ptr + d.begin + 1, d.array.ptr + d.begin, offset * (void*).sizeof);
280 if (int offset = d.end - (to + 1))
281 memmove(d.array.ptr + to + 2, d.array.ptr + to + 1, offset * (void*).sizeof);
282 ++d.begin;
283 ++d.end;
284 ++to;
285 }
286 } else {
287 if (d.begin == 0 || 3 * (from - to) < 2 * (d.end - d.begin)) {
288 memmove(d.array.ptr + to + 1, d.array.ptr + to, (from - to) * (void*).sizeof);
289 } else {
290 // optimization
291 if (int offset = to - d.begin)
292 memmove(d.array.ptr + d.begin - 1, d.array.ptr + d.begin, offset * (void*).sizeof);
293 if (int offset = d.end - (from + 1))
294 memmove(d.array.ptr + from, d.array.ptr + from + 1, offset * (void*).sizeof);
295 --d.begin;
296 --d.end;
297 --to;
298 }
299 }
300 d.array.ptr[to] = t;
301 }
302
303 void **erase(void **xi)
304 {
305 // Q_ASSERT(d.ref_ == 1);
306 int i = xi - (d.array.ptr + d.begin);
307 remove(i);
308 return d.array.ptr + d.begin + i;
309 }
310
311 int size() const { return d.end - d.begin; }
312 bool isEmpty() const { return d.end == d.begin; }
313 const (void*)* at(int i) const { return d.array.ptr + d.begin + i; }
314 const (void*)* begin() const { return d.array.ptr + d.begin; }
315 const (void*)* end() const { return d.array.ptr + d.end; }
316 }
317
318 import std.stdio;
319 import std.conv;
320
321 alias void Dummy; // DMD bug #3538
322
323 struct QList(T, alias Default = Dummy)
324 {
325 static if (is(Default == Dummy))
326 alias QTypeInfo!T TI;
327 else
328 alias Default TI;
329
330 struct Node
331 {
332 void *v;
333
334 static if (isQObjectType!T || isObjectType!T || isValueType!T || is(T == string)) // binded Qt types
335 {
336 T t()
337 {
338 static if(is(T == string))
339 {
340 void* ptr = cast(void*)(TI.isLarge || TI.isStatic ? v : &this);
341 return QStringUtil.toNativeString(ptr);
342 }
343 else static if (isValueType!T)
344 {
345 void* ptr = cast(void*)(isLarge!T() || isStatic!T() ? v : &this);
346 return new T(ptr, QtdObjectFlags.nativeOwnership);
347 }
348 else
349 {
350 return T.__getObject( *cast(void**)(&this) );
351 }
352 }
353 }
354 else // native types
355 {
356 ref T t()
357 {
358 static if(TI.isLarge || TI.isStatic)
359 return *cast(T*)(this.v);
360 else
361 return *cast(T*)(&this);
362 }
363 }
364 }
365
366 union {
367 QListData p;
368 QListData.Data* d;
369 }
370
371 public:
372 /*
373 void output()
374 {
375 writeln("QList atomic ", d.ref_.load());
376 }
377 */
378
379 static QList!T opCall()
380 {
381 QList!T res;
382 // writeln("QList opCall");
383
384 res.d = &QListData.shared_null;
385 res.d.ref_.increment();
386
387 return res;
388 }
389
390 this(this)
391 {
392 // writeln("QList postblit");
393 d.ref_.increment();
394 if (!d.sharable)
395 detach_helper();
396 }
397
398 ~this()
399 {
400 // writeln("QList ~this");
401 if (d && !d.ref_.decrement())
402 free(d);
403 }
404
405 ref QList!T opAssign(const ref QList!T l)
406 {
407 // writeln("QList opAssign");
408 if (d != l.d) {
409 QListData.Data* nd = cast(QListData.Data*)l.d;
410 nd.ref_.increment();
411 if (!d.ref_.decrement())
412 free(d);
413 d = nd;
414 if (!d.sharable)
415 detach_helper();
416 }
417 return this;
418 }
419
420 int length() const { return p.size(); }
421 int size() const { return length; }
422
423 void detach() { if (d.ref_.load() != 1) detach_helper(); }
424
425 private void detach_helper()
426 {
427 Node *n = cast(Node*)(p.begin());
428 QListData.Data* x = p.detach2();
429 node_copy(cast(Node*)(p.begin()), cast(Node*)(p.end()), n);
430 if (!x.ref_.decrement())
431 free(x);
432 }
433
434 void append(const T t) // fix to const ref for complex types TODO
435 {
436 detach();
437 static if (isQObjectType!T || isObjectType!T || isValueType!T)
438 {
439 node_construct(cast(Node*)(p.append()), t);
440 }
441 else
442 {
443 const T cpy = t;
444 node_construct(cast(Node*)(p.append()), cpy);
445 }
446 }
447
448 alias append opCatAssign;
449
450 static if (isQObjectType!T || isObjectType!T || isValueType!T || is(T == string))
451 {
452 T at(int i) const
453 {
454 assert(i >= 0 && i < p.size(), "QList!T.at(): index out of range");
455 return (cast(Node*)(p.at(i))).t();
456 }
457 T opIndex(int i)
458 {
459 assert(i >= 0 && i < p.size(), "QList!T.at(): index out of range");
460 return (cast(Node*)(p.at(i))).t();
461 }
462 }
463 else
464 {
465 const (T) at(int i) const // DMD BUG
466 {
467 assert(i >= 0 && i < p.size(), "QList!T.at(): index out of range");
468 return (cast(Node*)(p.at(i))).t();
469 }
470 ref T opIndex(int i)
471 {
472 assert(i >= 0 && i < p.size(), "QList!T.at(): index out of range");
473 return (cast(Node*)(p.at(i))).t();
474 }
475 }
476
477 static if (isQObjectType!T || isObjectType!T || isValueType!T) //binded types
478 void node_construct(Node *n, const T t)
479 {
480 static if (isValueType!T)
481 {
482 if (isLarge!T() || isStatic!T()) // TODO should be static if
483 n.v = T.__constructNativeCopy(t.__nativeId); // n.v = new T(t);
484 else if (isComplex!T())
485 T.__constructPlacedNativeCopy(t.__nativeId, n); // new (n) T(t);
486 else
487 T.__constructPlacedNativeCopy(t.__nativeId, n); // TODO should be *cast(T*)(n) = cast(T)(t); as it is a primitive type. fix when they are implemented with structs
488 }
489 else // in case of QObject or Object Qt types we place a pointer to a native object in the node
490 n = cast(Node*) t.__nativeId;
491 }
492 else static if (is(T == string))
493 {
494 void node_construct(Node *n, T t)
495 {
496 QString.__constructPlacedQString(n, t);
497 }
498 }
499 else // native types
500 void node_construct(Node *n, const ref T t)
501 {
502 static if (TI.isLarge || TI.isStatic)
503 n.v = q_new!T(t); // n.v = new T(t);
504 else static if (TI.isComplex)
505 q_new_at(n, t); // new (n) T(t);
506 else
507 *cast(T*)(n) = cast(T)(t);
508 }
509
510 void node_copy(Node *from, Node *to, Node *src)
511 {
512 // writeln("QList node_copy");
513 static if (isQObjectType!T || isObjectType!T)
514 {} // ensure to do nothing. copying only a pointer
515 else static if(is(T == string))
516 {
517 while(from != to) // TODO when porting to Qt 5 ensure that QTypeInfo<QString>.isLarge and .isStatic == false
518 QString.__constructPlacedNativeCopy(src++, from++); // new (from++) T(*reinterpret_cast<T*>(src++));
519 }
520 else static if (isValueType!T)
521 {
522 if (TI.isLarge || TI.isStatic) // TODO should be static if
523 while(from != to)
524 (from++).v = T.__constructNativeCopy((src++).v); // (from++)->v = new T(*reinterpret_cast<T*>((src++)->v));
525 else if (TI.isComplex)
526 while(from != to)
527 T.__constructPlacedNativeCopy(src++, from++); // new (from++) T(*reinterpret_cast<T*>(src++));
528 }
529 else static if (TI.isLarge || TI.isStatic)
530 while(from != to)
531 (from++).v = q_new!T(*cast(T*)((src++).v));
532 else static if (TI.isComplex)
533 while(from != to)
534 q_new_at(from++, *cast(T*)(src++));
535 }
536
537 T[] toArray()
538 {
539 T[] res;
540 res.length = this.length;
541 for(int i = 0; i < res.length; ++i)
542 {
543 static if (isValueType!T)
544 res[i] = new T(T.__constructNativeCopy(this.at(i).__nativeId)); // Node should probably provide a ptr method to directly extract pointer to the native value stored in the list to avoid creating a dummy D object in t()
545 else
546 res[i] = this.opIndex(i);
547 }
548 return res;
549 }
550
551 void free(QListData.Data* data)
552 {
553 // writeln("QList data destroyed");
554 node_destruct(cast(Node*)(data.array.ptr + data.begin),
555 cast(Node*)(data.array.ptr + data.end));
556 if (data.ref_.load() == 0)
557 qFree(data);
558 }
559
560 void node_destruct(Node *from, Node *to)
561 {
562 static if (isQObjectType!T || isObjectType!T) //binded types
563 {} // removing just pointers, do nothing
564 else static if (is(T == string))
565 {
566 while (from != to)
567 --to, QString.__callNativeDestructor(to);
568 }
569 else static if (isValueType!T) //binded value types
570 {
571 if (isLarge!T() || isStatic!T()) // TODO should be static if
572 while (from != to)
573 --to, T.__deleteNativeObject(to.v);
574 else if (isComplex!T())
575 while (from != to)
576 --to, T.__callNativeDestructor(to);
577 }
578 else
579 {
580 static if (TI.isLarge || TI.isStatic)
581 while (from != to) --to, q_delete(cast(T*)(to.v));
582 else static if (TI.isComplex)
583 while (from != to) --to, cast(T*)(to).__dtor();
584 }
585 }
586
587 //iteration support
588 int opApply(int delegate(ref T) dg)
589 {
590 int result = 0;
591 int sz = this.length;
592 for (int i = 0; i < sz; i++)
593 {
594 static if (isQtReference!T)
595 {
596 T t = this[i]; // hack to avoid "is not an lvalue" error, since dg accepts ref T
597 result = dg(t);
598 }
599 else
600 result = dg(this[i]);
601
602 if (result)
603 break;
604 }
605 return result;
606 }
607 }
608
609 alias QList!string QStringList;
610
611 QList!T toQList(T)(T[] src)
612 {
613 auto res = QList!T.opCall();
614 foreach(elem; src)
615 res.append(elem);
616 return res;
617 }
618
619 QList!T qList(T)()
620 {
621 return QList!T.opCall();
622 }
623
624 extern(C) void qtd_create_QList(void *nativeId);
625 extern(C) void qtd_create_QList_double(void *nativeId);
626
627 extern(C) void qtd_create_QList_QObject(void *nativeId);