This repository has been archived by the owner on Oct 15, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 123
/
Copy pathkeyset.c
3018 lines (2690 loc) · 74.6 KB
/
keyset.c
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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* @file
*
* @brief Methods for key sets.
*
* @copyright BSD License (see LICENSE.md or https://www.libelektra.org)
*/
#include "kdbprivate.h"
#include <kdb.h>
#ifdef HAVE_KDBCONFIG_H
#include "kdbconfig.h"
#endif
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <kdbtypes.h>
#include "kdbinternal.h"
#include <kdbassert.h>
#include <kdbrand.h>
#define ELEKTRA_MAX_PREFIX_SIZE sizeof ("namespace/")
#define ELEKTRA_MAX_NAMESPACE_SIZE sizeof ("system")
static void elektraOpmphmCopy (struct _KeySetData * dest ELEKTRA_UNUSED, const struct _KeySetData * source ELEKTRA_UNUSED);
/**
* @internal
*
* @brief helper function that copies a KeySetData object, without copying the reference counter.
*/
static struct _KeySetData * keySetDataCopy (const struct _KeySetData * original)
{
struct _KeySetData * copy = keySetDataNew ();
copy->alloc = original->alloc;
copy->size = original->size;
copy->isOpmphmInvalid = original->isOpmphmInvalid;
// We don't need to copy the isInMmap flag, because copied data is never in mmap
if (original->alloc > 0)
{
copy->array = elektraMalloc (original->alloc * sizeof (struct _Key *));
memcpy (copy->array, original->array, original->alloc * sizeof (struct _Key *));
for (size_t i = 0; i < copy->size; i++)
{
Key * k = copy->array[i];
keyIncRef (k);
}
}
elektraOpmphmCopy (copy, original);
return copy;
}
/**
* @internal
*
* @brief Helper method: Ensures, that the supplied keyset has its own KeySetData instance
*
* @post @p keyset's data field != NULL
*
* @param keyset the keyset to ensure it has its own KeySetData instance
*/
void keySetDetachData (KeySet * keyset)
{
if (!keyset)
{
return;
}
if (keyset->data == NULL)
{
keyset->data = keySetDataNew ();
keySetDataRefInc (keyset->data);
}
else if (keyset->data->refs > 1 || isKeySetDataInMmap (keyset->data))
{
struct _KeySetData * copied = keySetDataCopy (keyset->data);
keySetDataRefDecAndDel (keyset->data);
keyset->data = copied;
keySetDataRefInc (keyset->data);
}
}
/**
* @internal
*
* @brief KeySets OPMPHM cleaner.
*
* Must be invoked by every function that changes a Key name in a KeySet, adds a Key or
* removes a Key.
* Set also the KS_FLAG_NAME_CHANGE KeySet flag.
*
* @param ks the KeySet
*/
static void elektraOpmphmInvalidate (struct _KeySetData * ksdata ELEKTRA_UNUSED)
{
#ifdef ELEKTRA_ENABLE_OPTIMIZATIONS
ksdata->isOpmphmInvalid = true;
if (ksdata && ksdata->opmphm) opmphmClear (ksdata->opmphm);
#endif
}
/**
* @internal
*
* @brief KeySets OPMPHM and OPMPHM predictor copy.
*
* Should be invoked by every function making a copy of a KeySet.
*
* @param dest the destination KeySet
* @param source the source KeySet
*/
static void elektraOpmphmCopy (struct _KeySetData * dest ELEKTRA_UNUSED, const struct _KeySetData * source ELEKTRA_UNUSED)
{
#ifdef ELEKTRA_ENABLE_OPTIMIZATIONS
if (!source || !dest)
{
return;
}
// nothing to copy
// OPMPHM predictor
if (source->opmphmPredictor)
{
if (!dest->opmphmPredictor)
{
dest->opmphmPredictor = opmphmPredictorNew ();
}
if (dest->opmphmPredictor)
{
opmphmPredictorCopy (dest->opmphmPredictor, source->opmphmPredictor);
}
}
if (!opmphmIsBuild (source->opmphm))
{
return;
}
// OPMPHM
if (!dest->opmphm)
{
dest->opmphm = opmphmNew ();
}
if (dest->opmphm)
{
opmphmCopy (dest->opmphm, source->opmphm);
}
#endif
}
/** @class doxygenFlatCopy
*/
/**
* @defgroup keyset KeySet
* @brief Methods to manipulate KeySets.
*
* A KeySet is a set of keys.
*
* Most important properties of a KeySet:
*
* - Allows us to iterate over all keys (in any depth)
* - Iteration is always sorted
* - Fast key lookup
* - A Key may be shared among many KeySets.
*
* The most important methods of KeySet:
*
* - With ksNew() you can create a new KeySet.
* - You can append keys with ksAppendKey() or
* with ksAppend() you can append a whole keyset.
* - Using ksLookup() you can lookup (or pop with #KDB_O_POP) a key.
* - With ksGetSize() and ksAtCursor() you can iterate through the keyset.
* Be assured that you will get every key of the set in a stable
* order (parents before children).
*
* @copydetails doxygenFlatCopy
*
* KeySet is the most important data structure in Elektra. It makes it possible
* to get and store many keys at once inside the database. In addition to
* that, the class can be used as high level datastructure in applications
* and it can be used in plugins to manipulate or check configuration.
*
* With ksLookupByName() it is possible to fetch easily specific keys
* out of the list of keys.
*
* You can easily create and iterate keys:
*
* @snippet ksNewExample.c Full Example
*
* @par Copy-on-Write
*
* Keysets employ copy-on-write techniques to minimize memory footprint.
* If you create a copy or a duplication of a keyset, the resulting keyset
* initially references the same data as the source keyset.
* Only if add or remove keys from a keyset additional memory is allocated.
*
* @{
*/
/**
* Allocate, initialize and return a new KeySet object.
*
* Objects created with ksNew() must be destroyed with ksDel().
*
* You can use an arbitrary long list of parameters to preload the KeySet
* with a list of Keys. Either your first and only parameter is 0 or
* your last parameter must be KS_END.
*
* So, terminate with ksNew(0, KS_END) or ksNew(20, ..., KS_END)
*
* @warning Never use ksNew(0, keyNew(...), KS_END).
* If the first parameter is 0, other arguments are ignored.
*
* The first parameter @p alloc defines how many Keys can be added
* without reallocation.
* If you pass any alloc size greater than 0, but less than 16,
* it will default to 16.
*
* For most uses
*
* @snippet ksNew.c Simple
*
* will be fine. The alloc size will be 16 and will double whenever
* size reaches alloc size, so it also performs well with large KeySets.
*
* You can defer the allocation of the internal array that holds
* the Keys, by passing 0 as the alloc size. This is useful if it is
* unclear whether your KeySet will actually hold any Keys
* and you want to avoid a malloc call.
*
* @snippet ksNew.c No Allocation
*
* If the size of the KeySet is known in advance, use the
* @p alloc parameter to hint the size of the KeySet.
*
* If your application only needs up to 15 Keys you can request a KeySet of size 15:
*
* @snippet ksNew.c Length 15
*
* If you start having 3 Keys, and your application needs approximately
* 200 up to 500 Keys, you can use:
*
* @snippet ksNew.c Hint 500
*
* Alloc size is 500, the size of the KeySet will be 3 after ksNew.
* This means the KeySet will reallocate when appending more than
* 497 keys.
*
* The main benefit of taking a list of variant length parameters is to be able
* to have one C-Statement for any possible KeySet.
* If you prefer, you can always create an empty KeySet and use ksAppendKey().
*
* @post the KeySet is rewinded properly
*
* @param alloc gives a hint for how many Keys may be stored initially
*
* @return a ready to use KeySet object
* @retval 0 on memory error
*
* @since 1.0.0
* @see ksDel() to free the KeySet afterwards
* @see ksDup() to duplicate an existing KeySet
* @see ksAppendKey() to append individual Keys to a KeySet
*/
KeySet * ksNew (size_t alloc, ...)
{
KeySet * ks;
va_list va;
va_start (va, alloc);
ks = ksVNew (alloc, va);
va_end (va);
return ks;
}
/**
* @copydoc ksNew
*
* @pre caller must call va_start and va_end
* @par va the list of arguments
* @param alloc the allocation size
* @param va the list of variable arguments
**/
KeySet * ksVNew (size_t alloc, va_list va)
{
KeySet * keyset = 0;
keyset = (KeySet *) elektraCalloc (sizeof (KeySet));
if (!keyset)
{
return 0;
}
ksInit (keyset);
if (alloc == 0) return keyset;
keyset->data = keySetDataNew ();
keySetDataRefInc (keyset->data);
alloc++; /* for ending null byte */
if (alloc < KEYSET_SIZE)
keyset->data->alloc = KEYSET_SIZE;
else
keyset->data->alloc = alloc;
keyset->data->array = elektraCalloc (sizeof (struct _Key *) * keyset->data->alloc);
if (!keyset->data->array)
{
return 0;
}
keyset->data->array[0] = 0;
if (alloc != 0)
{
Key * key = (struct _Key *) va_arg (va, struct _Key *);
while (key)
{
ksAppendKey (keyset, key);
key = (struct _Key *) va_arg (va, struct _Key *);
}
}
elektraOpmphmInvalidate (keyset->data);
ksRewind (keyset); // ksAppendKey changed the internal cursor
return keyset;
}
/**
* Return a duplicate of a KeySet.
*
* Objects created with ksDup() must be destroyed with ksDel().
*
* Memory will be allocated as needed for dynamic properties,
* so you need to ksDel() the returned pointer.
*
* A flat copy is made, so the Keys will not be duplicated,
* but their reference counter is updated, so both KeySets
* need to be deleted via ksDel().
*
* @param source has to be an initialized KeySet
*
* @return a flat copy of source on success
* @retval 0 on NULL pointer
*
* @since 1.0.0
* @see ksNew() for creating a new KeySet
* @see ksDel() for deleting a KeySet
* @see keyDup() for Key duplication
*/
KeySet * ksDup (const KeySet * source)
{
if (!source) return 0;
KeySet * keyset = elektraCalloc (sizeof (KeySet));
keyset->data = source->data;
if (keyset->data != NULL)
{
keySetDataRefInc (keyset->data);
}
return keyset;
}
/**
* @internal
* @brief Deeply copies from source to dest.
*
* The keyset as well as its containing keys are duplicated.
* This means that you have to keyDel() the contained keys and
* ksDel() the returned keyset..
*
* the sync status will be as in the original KeySet
*
* @param source has to be an initialized source KeySet
* @return a deep copy of source on success
* @retval 0 on NULL pointer or a memory error happened
* @see ksNew(), ksDel()
* @see keyDup() for key duplication
* @see ksDup() for flat copy
*/
KeySet * ksDeepDup (const KeySet * source)
{
if (!source) return 0;
if (!source->data) return ksNew (0, KS_END);
size_t s = source->data->size;
size_t i = 0;
KeySet * keyset = 0;
keyset = ksNew (source->data->alloc, KS_END);
for (i = 0; i < s; ++i)
{
Key * k = source->data->array[i];
Key * d = keyDup (k, KEY_CP_ALL);
if (ksAppendKey (keyset, d) == -1)
{
ksDel (keyset);
return 0;
}
}
elektraOpmphmCopy (keyset->data, source->data);
return keyset;
}
/**
* Replace the content of a KeySet with another one.
*
* Most often you may want a duplicate of a KeySet, see
* ksDup() or append keys, see ksAppend().
* In some situations you need to copy Keys from a
* KeySet to another KeySet, for which this function
* exists.
*
* @note You can also use it to clear a KeySet when you pass
* a NULL pointer as @p source.
*
* @par Implementation:
* First all Keys in @p dest will be deleted. Afterwards
* the content of @p source will be added to the destination.
*
* A flat copy is made, so Keys will not be duplicated,
* but their reference counter is updated, so both KeySets
* need to be deleted via ksDel().
*
* @copydetails doxygenFlatCopy
*
* @code
int f (KeySet *ks)
{
KeySet *c = ksNew (20, ..., KS_END);
// c receives keys
ksCopy (ks, c); // pass the KeySet to the caller
ksDel (c);
} // caller needs to ksDel (ks)
* @endcode
*
* @param source an initialized KeySet or NULL
* @param dest an initialized KeySet, where the Keys from @p source get copied to
*
* @retval 1 on success
* @retval 0 if @p dest was cleared successfully (@p source is NULL)
* @retval -1 when @p dest is a NULL pointer
*
* @since 1.0.0
* @see ksNew() for creating a new KeySet
* @see ksDel() for deleting an existing KeySet
* @see ksDup() for duplicating an existing KeySet
* @see keyCopy() for copying Keys
*/
int ksCopy (KeySet * dest, const KeySet * source)
{
if (!dest) return -1;
if (!source)
{
ksClear (dest);
return 0;
}
if (dest->data != NULL)
{
keySetDataRefDecAndDel (dest->data);
}
dest->data = source->data;
if (dest->data != NULL)
{
keySetDataRefInc (dest->data);
}
return 1;
}
/**
* A destructor for KeySet objects.
*
* Every KeySet created by ksNew() must be deleted with ksDel().
*
* When the reference counter of @p ks is non-zero, this function
* will do nothing and simply return the current value of the
* reference counter.
*
* It is therefore safe to call `ksDel (ks)` on any `KeySet * ks`.
*
* @param ks the KeySet object to delete
*
* @retval 0 when the KeySet was freed
* @retval -1 on NULL pointers
* @return the value of the reference counter, if it was non-zero
*
* @since 1.0.0
* @see ksNew() for creating a new KeySet
* @see ksIncRef() for more information about the reference counter
*/
int ksDel (KeySet * ks)
{
if (ks == NULL)
{
return -1;
}
if (ks->refs > 0)
{
return ks->refs;
}
ksClose (ks);
if (!ks->isInMmap)
{
elektraFree (ks);
}
return 0;
}
/**
* @brief Empties a KeySet.
*
* This function
* - **does not** check or modify the reference count of `ks`
* - decrements the reference count of all keys contained in `ks`
* - deletes all keys that where only referenced by `ks`
* - resets size of `ks` to 0
*
* @param ks the KeySet to clear
*
* @retval 0 on success
* @retval -1 on failure (memory) or ks == NULL
*/
int ksClear (KeySet * ks)
{
if (ks == NULL) return -1;
ksClose (ks);
ks->data = keySetDataNew ();
keySetDataRefInc (ks->data);
if ((ks->data->array = elektraCalloc (sizeof (struct _Key *) * KEYSET_SIZE)) == 0)
{
ks->data->size = 0;
return -1;
}
ks->data->alloc = KEYSET_SIZE;
elektraOpmphmInvalidate (ks->data);
return 0;
}
/**
* Increment the reference counter of a KeySet object.
*
* As long as the reference counter is non-zero, `ksDel()` operations on @p key
* will be a no-op and return an error code.
*
* Elektra's system for reference counting is not based on a concept
* of shared ownership. It is more similar to a shared lock, where the counter
* is used to keep track of how many clients hold the lock.
*
* Initially, the reference counter will be 0. This can be interpreted as
* the lock being unlocked. When you increment the reference counter, the lock
* becomes locked and `ksDel()` is blocked and fails. Only when the reference
* counter is fully decremented back down to 0 again, will `ksDel()` work again.
*
* @note The reference counter can never exceed `UINT16_MAX - 1`. `UINT16_MAX` is
* reserved as an error code.
*
* @post @p ks's reference counter is > 0
* @post @p ks's reference counter is <= UINT16_MAX - 1
*
* @param ks the KeySet object whose reference counter should be increased
*
* @return the updated value of the reference counter
* @retval UINT16_MAX on NULL pointer
* @retval UINT16_MAX when the reference counter already was the maximum value `UINT16_MAX - 1`,
* the reference counter will not be modified in this case
*
* @since 1.0.0
* @see ksGetRef() to retrieve the current reference count
* @see ksDecRef() for decreasing the reference counter
* @see ksDel() for deleting a Key
*/
uint16_t ksIncRef (KeySet * ks)
{
if (ks == NULL)
{
return UINT16_MAX;
}
if (ks->refs == UINT16_MAX - 1)
{
return UINT16_MAX;
}
return ++ks->refs;
}
/**
* Decrement the reference counter of a KeySet object.
*
* As long as the reference counter is non-zero, `ksDel()` operations on @p key
* will be a no-op and return an error code.
*
* @param key the KeySet object whose reference counter should get decreased
*
* @return the updated value of the reference counter
* @retval UINT16_MAX on NULL pointer
* @retval 0 when the reference counter already was the minimum value 0,
* the reference counter will not be modified in this case
*
* @since 1.0.0
* @see ksGetRef() to retrieve the current reference count
* @see ksIncRef() for increasing the reference counter and for a more complete
* explanation of the reference counting system
* @see ksDel() for deleting a Key
*/
uint16_t ksDecRef (KeySet * ks)
{
if (ks == NULL)
{
return UINT16_MAX;
}
if (ks->refs == 0)
{
return 0;
}
return --ks->refs;
}
/**
* Return the current reference counter value of a KeySet object.
*
* @param ks the KeySet whose reference counter to retrieve
*
* @return the value of the @p key's reference counter
* @retval -1 on NULL pointer
*
* @since 1.0.0
* @see ksIncRef() for increasing the reference counter and for a more complete
* explanation of the reference counting system
* @see ksDecRef() for decreasing the reference counter
**/
uint16_t ksGetRef (const KeySet * ks)
{
if (ks == NULL)
{
return UINT16_MAX;
}
return ks->refs;
}
/**
* @brief Compare by unescaped name only (not by owner, they are equal)
*
* @internal
*
* Other non-case Cmp* are based on this one.
*
* Is suitable for binary search (but may return wrong owner)
*
*/
static int keyCompareByName (const void * p1, const void * p2)
{
Key * k1 = *(Key **) p1;
Key * k2 = *(Key **) p2;
int k1Shorter = k1->keyName->keyUSize < k2->keyName->keyUSize;
size_t size = k1Shorter ? k1->keyName->keyUSize : k2->keyName->keyUSize;
int cmp = memcmp (k1->keyName->ukey, k2->keyName->ukey, size);
if (cmp != 0 || k1->keyName->keyUSize == k2->keyName->keyUSize)
{
return cmp;
}
return k1Shorter ? -1 : 1;
}
/**
* Compare the name of two Keys.
*
* The comparison is based on a memcmp of the Key's names.
* If the names match, the Keys are found to be exactly the
* same and 0 is returned. These two keys can't be used in the same
* KeySet.
*
* keyCmp() defines the sorting order for a KeySet.
*
* The following 3 points are the rules for NULL values:
*
* - A NULL pointer will be found to be smaller than every other
* Key. If both are NULL pointers, 0 is returned.
*
* - A NULL name will be found to be smaller than every other
* name. If both are NULL names, 0 is returned.
*
* If the name is equal then:
*
* - No owner will be found to be smaller than every other owner.
* If both don't have an owner, 0 is returned.
*
* @note the owner will only be used if the names are equal.
*
* Given any Keys k1 and k2 constructed with keyNew(), following
* equation hold true:
*
* @snippet testabi_rel.c cmp null
*
* Here are some more examples:
* @code
Key *k1 = keyNew("user:/a", KEY_END);
Key *k2 = keyNew("user:/b", KEY_END);
// keyCmp(k1,k2) < 0
// keyCmp(k2,k1) > 0
* @endcode
*
*
* Do not strcmp the keyName() yourself, because
* the result differs from simple ascii comparison.
*
* @pre The Keys @p k1 and @p k2 have been properly initialized via keyNew() or are NULL
* @invariant All parts of the Keys remain unchanged
* @post If the result is 0, @p k1 and @p k2 cannot be used in the same KeySet
*
* @param k1 the first Key to be compared
* @param k2 the second Key to be compared
*
* @retval <0 if k1 < k2
* @retval 0 if k1 == k2
* @retval >0 if k1 > k2
*
* @since 1.0.0
* @ingroup keytest
* @see ksAppendKey(), ksAppend() will compare Keys via keyCmp() when appending
* @see ksLookup() will compare Keys via keyCmp() during searching
*/
int keyCmp (const Key * k1, const Key * k2)
{
if (!k1 && !k2) return 0;
if (!k1) return -1;
if (!k2) return 1;
if (!k1->keyName->key && !k2->keyName->key) return 0;
if (!k1->keyName->key) return -1;
if (!k2->keyName->key) return 1;
return keyCompareByName (&k1, &k2);
}
/**
* Return the number of Keys that @p ks contains.
*
* @param ks the KeySet object to get the size from
*
* @return the number of Keys that @p ks contains.
* @retval -1 on NULL pointer
*
* @since 1.0.0
*/
ssize_t ksGetSize (const KeySet * ks)
{
if (!ks) return -1;
if (!ks->data) return 0;
return ks->data->size;
}
/*******************************************
* Filling up KeySets *
*******************************************/
/**
* @internal
*
* Binary search in a KeySet that informs where a key should be inserted.
*
* @code
ssize_t result = ksSearchInternal(ks, toAppend);
if (result >= 0)
{
ssize_t position = result;
// The key already exist in key set.
} else {
ssize_t insertpos = -result-1;
// The key was not found in key set.
}
* @endcode
*
* @param ks the keyset to work with
* @param toAppend the key to check
* @return position where the key is (>=0) if the key was found
* @return -insertpos -1 (< 0) if the key was not found
* so to get the insertpos simple do: -insertpos -1
*/
static ssize_t ksSearchInternal (const KeySet * ks, const Key * toAppend)
{
if (ks->data == NULL)
{
return -1;
}
ssize_t left = 0;
ssize_t right = ks->data->size;
--right;
register int cmpresult;
ssize_t middle = -1;
ssize_t insertpos = 0;
if (ks->data->size == 0)
{
return -1;
}
cmpresult = keyCompareByName (&toAppend, &ks->data->array[right]);
if (cmpresult > 0)
{
return -((ssize_t) ks->data->size) - 1;
}
cmpresult = 1;
while (1)
{
if (right < left)
{
/* Nothing was found */
break;
}
middle = left + ((right - left) / 2);
cmpresult = keyCompareByName (&toAppend, &ks->data->array[middle]);
if (cmpresult > 0)
{
insertpos = left = middle + 1;
}
else if (cmpresult == 0)
{
/* We have found it */
break;
}
else
{
insertpos = middle;
right = middle - 1;
}
}
if (!cmpresult)
{
return middle;
}
else
{
return -insertpos - 1;
}
}
/**
* Search in a key set, either yielding the actual index of the
* key, if the key has been found within the key set, or a negative
* value indicating the insertion index of the key, if the key
* would be inserted.
*
* @code
ssize_t result = ksSearch(ks, key);
if (result >= 0)
{
ssize_t position = result;
// The key already exist in key set.
} else {
ssize_t insertpos = -result-1;
// The key was not found in key set.
}
* @endcode
*
* @param ks the keyset to work with
* @param key the key to check
* @return position where the key is (>=0) if the key was found
* @return -insertpos -1 (< 0) if the key was not found
* so to get the insertpos simple do: -insertpos -1
* @see ksLookup() for retrieving the found key
*/
ssize_t ksSearch (const KeySet * ks, const Key * key)
{
// TODO #4039 implement optimization
return ksSearchInternal (ks, key);
}
/**
* Appends a Key to the end of @p ks.
*
* Hands the ownership of the Key @p toAppend to the KeySet @p ks.
* ksDel(ks) uses keyDel(k) to delete every Key unless it got its reference counter
* incremented by keyIncRef(), e.g. by another KeySet that contains this Key.
*
* The reference counter of the Key will be incremented
* to indicate this ownership, and thus @p toAppend is not const.
*
* @copydetails doxygenFlatCopy
*
* @see keyGetRef()
*
* If the Key's name already exists in the KeySet, it will be replaced with
* the new Key.
*
* ksAppendKey() will also lock the Key's name from `toAppend`.
* This is necessary so that the order of the KeySet cannot
* be destroyed via calls to keySetName().
*
* The KeySet internal cursor will be set to the new Key.
*
* It is safe to directly append newly created Keys:
* @snippet keyset.c simple append
*
* If you want the key to outlive the KeySet, make sure to
* do proper ref counting:
* @snippet keyset.c ref append
*
* You can duplicate the Key to avoid aliasing,
* but then the Key in the KeySet has another identity:
* @snippet keyset.c dup append
*
* @param ks KeySet where @p toAppend should be append
* @param toAppend Key that will be appended to @p ks or deleted
*
* @return the size of the KeySet after appending
* @retval -1 on NULL pointers
* @retval -1 if appending failed (only on memory problems). The Key will be
* deleted then.
*
* @since 1.0.0
* @see ksAppend() for appending a KeySet to another KeySet
* @see keyIncRef() for manually increasing a Key's reference counter
*/
ssize_t ksAppendKey (KeySet * ks, Key * toAppend)
{
ssize_t result = -1;
if (!ks) return -1;
if (!toAppend) return -1;
if (!toAppend->keyName->key)
{
// needed for ksAppendKey(ks, keyNew(0))
keyDel (toAppend);
return -1;
}
keySetDetachData (ks);
keyLock (toAppend, KEY_LOCK_NAME);
result = ksSearchInternal (ks, toAppend);
if (result >= 0)
{
/* Seems like the key already exist. */
if (toAppend == ks->data->array[result])
{
/* user tried to insert the key with same identity */
return ks->data->size;
}
/* Pop the key in the result */
keyDecRef (ks->data->array[result]);
keyDel (ks->data->array[result]);
/* And use the other one instead */