$darkmode
Elektra 0.11.0
|
Methods to manipulate KeySets. More...
Macros | |
#define | KS_END ((Key *) 0) |
End of a list of keys. More... | |
Enumerations | |
enum | elektraLookupFlags { KDB_O_NONE = 0 , KDB_O_DEL = 1 , KDB_O_POP = 1 << 1 } |
Options to change the default behavior of ksLookup() functions. More... | |
Functions | |
KeySet * | ksNew (size_t alloc,...) |
Allocate, initialize and return a new KeySet object. More... | |
KeySet * | ksVNew (size_t alloc, va_list va) |
Allocate, initialize and return a new KeySet object. More... | |
KeySet * | ksDup (const KeySet *source) |
Return a duplicate of a KeySet. More... | |
int | ksCopy (KeySet *dest, const KeySet *source) |
Replace the content of a KeySet with another one. More... | |
int | ksDel (KeySet *ks) |
A destructor for KeySet objects. More... | |
int | ksClear (KeySet *ks) |
Empties a KeySet. More... | |
uint16_t | ksIncRef (KeySet *ks) |
Increment the reference counter of a KeySet object. More... | |
uint16_t | ksDecRef (KeySet *ks) |
Decrement the reference counter of a KeySet object. More... | |
uint16_t | ksGetRef (const KeySet *ks) |
Return the current reference counter value of a KeySet object. More... | |
ssize_t | ksGetSize (const KeySet *ks) |
Return the number of Keys that ks contains. More... | |
ssize_t | ksSearch (const KeySet *ks, const Key *key) |
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. More... | |
ssize_t | ksAppendKey (KeySet *ks, Key *toAppend) |
Appends a Key to the end of ks . More... | |
ssize_t | ksAppend (KeySet *ks, const KeySet *toAppend) |
Append all Keys in toAppend to the end of the KeySet ks . More... | |
ssize_t | ksRename (KeySet *ks, const Key *root, const Key *newRoot) |
Moves all keys below root to below newRoot . More... | |
elektraCursor | ksFindHierarchy (const KeySet *ks, const Key *root, elektraCursor *end) |
Searches for the start and optionally end of the key hierarchy rooted at root in ks . More... | |
KeySet * | ksBelow (const KeySet *ks, const Key *root) |
Retrieves all Keys from KeySet ks that are below or at root . More... | |
KeySet * | ksCut (KeySet *ks, const Key *cutpoint) |
Cuts out all Keys from KeySet ks that are below or at cutpoint . More... | |
Key * | ksPop (KeySet *ks) |
Remove and return the last Key of ks . More... | |
int | ksRewind (KeySet *ks) |
Rewinds the KeySet internal cursor. More... | |
Key * | ksNext (KeySet *ks) |
Returns the next Key in a KeySet. More... | |
Key * | ksCurrent (const KeySet *ks) |
Return the current Key. More... | |
elektraCursor | ksGetCursor (const KeySet *ks) |
Get the internal cursor of the KeySet. More... | |
Key * | ksAtCursor (const KeySet *ks, elektraCursor pos) |
Return Key at given position pos . More... | |
int | ksSetCursor (KeySet *ks, elektraCursor cursor) |
Set the KeySet internal cursor to cursor . More... | |
Key * | ksLookup (KeySet *ks, Key *key, elektraLookupFlags options) |
Look for a Key contained in ks that matches the name of the key . More... | |
Key * | ksLookupByName (KeySet *ks, const char *name, elektraLookupFlags options) |
Convenience method to look for a Key contained in ks with name name . More... | |
ssize_t | ksSubtract (KeySet *total, const KeySet *sub) |
Remove all the keys in sub from total . More... | |
Methods to manipulate KeySets.
A KeySet is a set of keys.
Most important properties of a KeySet:
The most important methods of KeySet:
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:
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.
#define KS_END ((Key *) 0) |
enum elektraLookupFlags |
Options to change the default behavior of ksLookup() functions.
These options can be ORed. That is the |-Operator in C.
Enumerator | |
---|---|
KDB_O_NONE | No Option set.
|
KDB_O_DEL | Delete parentKey key in ksLookup().
|
KDB_O_POP | Pop Parent out of keyset key in ksLookup(). @see ksPop(). |
ssize_t ksAppend | ( | KeySet * | ks, |
const KeySet * | toAppend | ||
) |
Append all Keys in toAppend
to the end of the KeySet ks
.
toAppend
KeySet will be left unchanged.
If a Key is both in toAppend
and ks
, the Key in ks
will be overwritten.
ks | the KeySet that will receive the Keys |
toAppend | the KeySet that provides the Keys that will be transferred |
ks
after transfer -1 | on NULL pointers |
ssize_t ksAppendKey | ( | KeySet * | ks, |
Key * | toAppend | ||
) |
Appends a Key to the end of ks
.
Hands the ownership of the Key toAppend
to the KeySet 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 toAppend
is not const.
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:
If you want the key to outlive the KeySet, make sure to do proper ref counting:
You can duplicate the Key to avoid aliasing, but then the Key in the KeySet has another identity:
ks | KeySet where toAppend should be append |
toAppend | Key that will be appended to ks or deleted |
-1 | on NULL pointers |
-1 | if appending failed (only on memory problems). The Key will be deleted then. |
Key* ksAtCursor | ( | const KeySet * | ks, |
elektraCursor | pos | ||
) |
Return Key at given position pos
.
The position is a number starting from 0.
ks | the KeySet to get the Key from |
pos | the position of the Key that should be retrieved |
pos
on success NULL | on NULL pointer, negative cursor position or a position that does not lie within the KeySet ks |
KeySet* ksBelow | ( | const KeySet * | ks, |
const Key * | root | ||
) |
Retrieves all Keys from KeySet ks
that are below or at root
.
This function works very much like ksCut(). It returns an identical KeySet, but the Keys also remain in ks
ks | the Keyset to copy from |
root | the point where to copy from the Keyset |
root
. If root
exists, it will also be appended. int ksClear | ( | KeySet * | ks | ) |
Empties a KeySet.
This function
ks
ks
ks
ks
to 0ks | the KeySet to clear |
0 | on success |
-1 | on failure (memory) or ks == NULL |
int ksCopy | ( | KeySet * | dest, |
const KeySet * | source | ||
) |
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.
source
.dest
will be deleted. Afterwards the content of 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().
source | an initialized KeySet or NULL |
dest | an initialized KeySet, where the Keys from source get copied to |
1 | on success |
0 | if dest was cleared successfully (source is NULL) |
-1 | when dest is a NULL pointer |
Key* ksCurrent | ( | const KeySet * | ks | ) |
Return the current Key.
The returned pointer is NULL if you reached the end or after ksRewind().
ks | the KeySet object to get the current Key from |
ks's
cursor 0 | on NULL pointer |
KeySet* ksCut | ( | KeySet * | ks, |
const Key * | cutpoint | ||
) |
Cuts out all Keys from KeySet ks
that are below or at cutpoint
.
Searches for the cutpoint
inside the KeySet ks
. If found, it cuts out this Key and everything which is below (see keyIsBelow()) this Key. These Keys will be missing in the keyset ks
. Instead, they will be moved to the returned KeySet. If cutpoint
is not found an empty KeySet is returned and ks
is not changed.
The cursor will stay at the same Key as it was before. If the cursor was inside the region of cut (moved) Keys, the cursor will be set to the Key before the cutpoint
.
If you use ksCut() on a KeySet you got from kdbGet() and plan to use kdbSet() later, make sure that you keep all Keys that should not be removed permanently. You have to keep the KeySet that was returned and the KeySet ks
.
You have the keyset ks:
system:/mountpoint/interest
system:/mountpoint/interest/folder
system:/mountpoint/interest/folder/key1
system:/mountpoint/interest/folder/key2
system:/mountpoint/other/key1
When you use
Then in returned
are:
system:/mountpoint/interest
system:/mountpoint/interest/folder
system:/mountpoint/interest/folder/key1
system:/mountpoint/interest/folder/key2
And in ks
are:
system:/mountpoint/other/key1
So kdbSet() permanently removes all keys at or below system:/mountpoint/interest
.
ks | the Keyset to cut. It will be modified by removing all Keys at or below the cutpoint. |
cutpoint | the point where to cut out the Keyset |
0 | on NULL pointers, no Key name or allocation problems |
uint16_t ksDecRef | ( | KeySet * | ks | ) |
Decrement the reference counter of a KeySet object.
As long as the reference counter is non-zero, ksDel()
operations on key
will be a no-op and return an error code.
key | the KeySet object whose reference counter should get decreased |
UINT16_MAX | on NULL pointer |
0 | when the reference counter already was the minimum value 0, the reference counter will not be modified in this case |
int ksDel | ( | KeySet * | ks | ) |
A destructor for KeySet objects.
Every KeySet created by ksNew() must be deleted with ksDel().
When the reference counter of 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
.
ks | the KeySet object to delete |
0 | when the KeySet was freed |
-1 | on NULL pointers |
KeySet* ksDup | ( | const KeySet * | source | ) |
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().
source | has to be an initialized KeySet |
0 | on NULL pointer |
elektraCursor ksFindHierarchy | ( | const KeySet * | ks, |
const Key * | root, | ||
elektraCursor * | end | ||
) |
Searches for the start and optionally end of the key hierarchy rooted at root
in ks
.
The hierarchy will only contain keys in the same namespace as root
. If root
is a cascading key, only cascading keys will be part of the hierarchy.
The main use-case for this function is this kind of loop:
ks | The keyset to search in |
root | The root of the hierachy to find |
end | If this is not NULL, it will be set to position of the first key after root that is not below root . This is useful for loops like the one above. If not keys below root exist in ks , end will always be set to the size of ks . This way a loop like the one above will still work correctly. |
-1 | if ks or root are NULL |
root
itself or the first key below root
that is part of ks
. If no keys below root
exist in ks
, the size of ks
is returned. The snippet above shows why this is useful. elektraCursor ksGetCursor | ( | const KeySet * | ks | ) |
Get the internal cursor of the KeySet.
With the cursors it is possible to read ahead in a KeySet:
It can also be used to restore the state of a KeySet in a function
It is of course possible to make the KeySet const and cast its const away to set the cursor. Another way to achieve the same is to ksDup() the KeySet, but it is not as efficient.
An invalid cursor will be returned directly after ksRewind(). When you set an invalid cursor ksCurrent() is 0.
You can also use the cursor directly by initializing it to some index in the KeySet and then incrementing or decrementing it, to iterate over the KeySet.
You can also use a while loop if you need access to the last cursor position.
ks | the KeySet object to get the cursor from |
-1 | on NULL pointer |
-1 | on an invalid internal cursor or after ksRewind |
uint16_t ksGetRef | ( | const KeySet * | ks | ) |
Return the current reference counter value of a KeySet object.
ks | the KeySet whose reference counter to retrieve |
key's
reference counter -1 | on NULL pointer |
ssize_t ksGetSize | ( | const KeySet * | ks | ) |
Return the number of Keys that ks
contains.
ks | the KeySet object to get the size from |
ks
contains. -1 | on NULL pointer |
uint16_t ksIncRef | ( | KeySet * | ks | ) |
Increment the reference counter of a KeySet object.
As long as the reference counter is non-zero, ksDel()
operations on 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.
UINT16_MAX - 1
. UINT16_MAX
is reserved as an error code.ks's
reference counter is > 0 ks's
reference counter is <= UINT16_MAX - 1ks | the KeySet object whose reference counter should be increased |
UINT16_MAX | on NULL pointer |
UINT16_MAX | when the reference counter already was the maximum value UINT16_MAX - 1 , the reference counter will not be modified in this case |
Key* ksLookup | ( | KeySet * | ks, |
Key * | key, | ||
elektraLookupFlags | options | ||
) |
Look for a Key contained in ks
that matches the name of the key
.
/
). Furthermore, a lookup should be done for every Key (also when iterating over Keys) so that the specifications are honored correctly. Keys of all namespaces need to be present so that ksLookup() can work correctly, so make sure to also use kdbGet() with a cascading Key.ksLookup() is designed to let you work with a KeySet containing all Keys of the application. The idea is to fully kdbGet() the whole configuration of your application and process it all at once with many ksLookup()
.
This function is efficient (at least using binary search). Together with kdbGet(), which you can use to load the whole configuration, you can write very effective and short code for configuration:
This is the way programs should get their configuration and search for the values. It is guaranteed, that more namespaces can be added easily and that all values can be set by admin and user. Furthermore, using the kdb-tool, it is possible to introspect which values an application will get (by doing the same cascading lookup).
If found, a pointer to the Key is returned. If not found a NULL pointer is returned.
Cascading lookups will by default search in all namespaces (proc:/, dir:/, user:/ and system:/), but will also correctly consider the specification (=metadata) in spec:/:
override/#
will make sure that another Key is considered beforenamespace/#
will change the number and/or order in which the namespaces are searchedfallback/#
will search for other Keys when the other possibilities up to now were not successfuldefault
to return the given value when not even fallback
Keys were found.This process is very flexible, but it would be boring to manually follow all this links to find out which Key will be taken in the end. Use kdb get -v
to trace the Keys.
This is also a nice example how a complete application with ksLookup() can look like.
ENABLE_OPTIMIZATIONS=ON
a hybrid search decides dynamically between the binary search and the OPMPHM. The hybrid search can be overruled by passing KDB_O_OPMPHM or KDB_O_BINSEARCH in the options to ksLookup().ks | the KeySet that should be searched |
key | the Key object you are looking for |
options | of type elektraLookupFlags with some KDB_O_* option bits - as explained above |
0 | if no Key has been found |
0 | on NULL pointers |
Key* ksLookupByName | ( | KeySet * | ks, |
const char * | name, | ||
elektraLookupFlags | options | ||
) |
Convenience method to look for a Key contained in ks
with name name
.
There are several options that can be used in conjunction with this function. All possible option flags can be found in elektraLookupFlags
ks | the KeySet that should be searched |
name | name of the Key you are looking for |
options | some KDB_O_* option bits (KDB_O_POP, KDB_O_DEL):
|
0 | if no Key has been found |
0 | on NULL pointers |
KeySet* ksNew | ( | size_t | alloc, |
... | |||
) |
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)
The first parameter 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
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.
If the size of the KeySet is known in advance, use the 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:
If you start having 3 Keys, and your application needs approximately 200 up to 500 Keys, you can use:
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().
alloc | gives a hint for how many Keys may be stored initially |
0 | on memory error |
Key* ksNext | ( | KeySet * | ks | ) |
Returns the next Key in a KeySet.
KeySets have an internal cursor that can be reset with ksRewind(). Every time ksNext() is called, the cursor is incremented and the new current Key is returned.
You'll get a NULL pointer if the Key at the end of the KeySet has been reached. On subsequent calls of ksNext() it will still return the NULL pointer.
The ks
internal cursor will be changed, so it is not const.
ks | the KeySet object to work with |
0 | when the end of the KeySet has been reached |
0 | on NULL pointer |
Key* ksPop | ( | KeySet * | ks | ) |
Remove and return the last Key of ks
.
The reference counter of the Key will be decremented by one.
The KeySet's cursor will not be affected if it did not point to the popped Key.
ks | KeySet to pop a Key from |
ks
NULL | if ks is empty or a NULL pointer |
ssize_t ksRename | ( | KeySet * | ks, |
const Key * | root, | ||
const Key * | newRoot | ||
) |
Moves all keys below root
to below newRoot
.
Only keys below root
will be modified. The rest of ks
remains untouched.
This functions is similar to the following snippet, but there are some differences.
Firstly, the optimizations only work, if ks
doesn't contain any keys below newRoot
that aren't below root
. If such keys exist, ksRename() will still work, but it will fall back to code similar to the for-loop above.
The second difference is that ksRename() will modify the keys in ks
directly, if they aren't referenced from anywhere else (if their reference count is 1 (see keyGetRef())). Normally, this shouldn't cause problems, but if you have a direct Key *
pointer to a key in ks
or hold a reference to some data within a key of ks
, you may need to call keyIncRef() to ensure the key isn't modified.
ks | the keyset to manipulate |
root | the old prefix that will be removed, must not be a cascading key |
newRoot | the new prefix the will replace the old one, must not be a cascading key |
-1 | if any of ks , root , newRoot is NULL, or if root or newRoot are cascading keys |
-2 | if ks already contains keys below newRoot |
0 | if ks contains no keys below root (and also not root itself) |
int ksRewind | ( | KeySet * | ks | ) |
Rewinds the KeySet internal cursor.
Use it to set the cursor to the beginning of the KeySet. ksCurrent() will always return NULL afterwards. So you want to use ksNext() first.
ks | the KeySet that should be rewound |
0 | on success |
-1 | on NULL pointer |
ssize_t ksSearch | ( | const KeySet * | ks, |
const Key * | key | ||
) |
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.
ks | the keyset to work with |
key | the key to check |
int ksSetCursor | ( | KeySet * | ks, |
elektraCursor | cursor | ||
) |
Set the KeySet internal cursor to cursor
.
Use it to set the cursor to a stored position. ksCurrent() will then return the Key at the position of the supplied cursor.
An invalid cursor will set the KeySet to its beginning like ksRewind(). When you set an invalid cursor ksCurrent() is 0.
ks | the KeySet object where the cursor should be set |
cursor | the cursor to set for ks |
0 | when the KeySet has been ksRewind()ed |
1 | otherwise |
-1 | on NULL pointer |
ssize_t ksSubtract | ( | KeySet * | total, |
const KeySet * | sub | ||
) |
Remove all the keys in sub
from total
.
total | the keyset where the keys should be removed from |
sub | the keys to remove |
-1 | on NULL pointers |
the | number of keys removed from total |
KeySet* ksVNew | ( | size_t | alloc, |
va_list | va | ||
) |
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)
The first parameter 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
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.
If the size of the KeySet is known in advance, use the 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:
If you start having 3 Keys, and your application needs approximately 200 up to 500 Keys, you can use:
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().
alloc | gives a hint for how many Keys may be stored initially |
0 | on memory error |
alloc | the allocation size |
va | the list of variable arguments |