-
Notifications
You must be signed in to change notification settings - Fork 7
/
sarray2.h
139 lines (133 loc) · 4.28 KB
/
sarray2.h
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
/**
* Sparse Array
*
* Author: David Chisnall
*
* License: See COPYING.MIT
*
*/
#ifndef _SARRAY_H_INCLUDED_
#define _SARRAY_H_INCLUDED_
#include <stdint.h>
#include <stdlib.h>
#include "visibility.h"
/**
* Sparse arrays, used to implement dispatch tables. Current implementation is
* quite RAM-intensive and could be optimised. Maps 32-bit integers to pointers.
*
* Note that deletion from the array is not supported. This allows accesses to
* be done without locking; the worst that can happen is that the caller gets
* an old value (and if this is important to you then you should be doing your
* own locking). For this reason, you should be very careful when deleting a
* sparse array that there are no references to it held by other threads.
*/
typedef struct
{
/**
* Mask value applied to the index when generating an index in this
* sub-array.
*/
uint32_t mask;
/**
* Number of bits that the masked value should be right shifted by to get
* the index in the subarray. If this value is greater than zero, then the
* value in the array is another SparseArray*.
*/
uint32_t shift;
/**
* The reference count for this. Used for copy-on-write. When making a
* copy of a sparse array, we only copy the root node, and increment the
* reference count of the remaining nodes. When modifying any leaf node,
* we copy if its reference count is greater than one.
*/
uint32_t refCount;
/**
* The data stored in this sparse array node.
*/
void ** data;
} SparseArray;
/**
* Turn an index in the array into an index in the current depth.
*/
#define MASK_INDEX(index) \
((index & sarray->mask) >> sarray->shift)
#define SARRAY_EMPTY ((void*)0)
/**
* Look up the specified value in the sparse array. This is used in message
* dispatch and so has been put in the header to allow compilers to inline it,
* even though this breaks the abstraction.
*/
static inline void* SparseArrayLookup(SparseArray * sarray, uint32_t index)
{
// This unrolled version of the commented-out segment below only works with
// sarrays that use one-byte leaves. It's really ugly, but seems to be faster.
// With this version, we get the same performance as the old GNU code, but
// with about half the memory usage.
uint32_t i = index;
switch (sarray->shift)
{
default: UNREACHABLE("broken sarray");
case 0:
return sarray->data[i & 0xff];
case 8:
return
((SparseArray*)sarray->data[(i & 0xff00)>>8])->data[(i & 0xff)];
case 16:
return
((SparseArray*)((SparseArray*)
sarray->data[(i & 0xff0000)>>16])->
data[(i & 0xff00)>>8])->data[(i & 0xff)];
case 24:
return
((SparseArray*)((SparseArray*)((SparseArray*)
sarray->data[(i & 0xff000000)>>24])->
data[(i & 0xff0000)>>16])->
data[(i & 0xff00)>>8])->data[(i & 0xff)];
}
/*
while(sarray->shift > 0)
{
uint32_t i = MASK_INDEX(index);
sarray = (SparseArray*) sarray->data[i];
}
uint32_t i = index & sarray->mask;
return sarray->data[i];
*/
}
/**
* Create a new sparse array.
*/
SparseArray *SparseArrayNew();
/**
* Creates a new sparse array with the specified capacity. The depth indicates
* the number of bits to use for the key. Must be a value between 8 and 32 and
* should ideally be a multiple of base_shift.
*/
SparseArray *SparseArrayNewWithDepth(uint32_t depth);
/**
* Returns a new sparse array created by adding this one as the first child
* node in an expanded one.
*/
SparseArray *SparseArrayExpandingArray(SparseArray *sarray);
/**
* Insert a value at the specified index.
*/
void SparseArrayInsert(SparseArray * sarray, uint32_t index, void * value);
/**
* Destroy the sparse array. Note that calling this while other threads are
* performing lookups is guaranteed to break.
*/
void SparseArrayDestroy(SparseArray * sarray);
/**
* Iterate through the array. Returns the next non-NULL value after index and
* sets index to the following value. For example, an array containing values
* at 0 and 10 will, if called with index set to 0 first return the value at 0
* and set index to 1. A subsequent call with index set to 1 will return the
* value at 10 and set index to 11.
*/
void * SparseArrayNext(SparseArray * sarray, uint32_t * index);
/**
* Creates a copy of the sparse array.
*/
SparseArray *SparseArrayCopy(SparseArray * sarray);
#endif //_SARRAY_H_INCLUDED_