sparse_array 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/ext/extconf.rb +2 -0
- data/ext/sp_ar.c +661 -0
- data/lib/sparse_array/version.rb +3 -0
- data/sparse_array.gemspec +22 -0
- metadata +86 -0
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2013 Sokolov Yura aka funny_falcon
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/ext/extconf.rb
ADDED
data/ext/sp_ar.c
ADDED
|
@@ -0,0 +1,661 @@
|
|
|
1
|
+
#include "ruby/ruby.h"
|
|
2
|
+
|
|
3
|
+
#include <stdio.h>
|
|
4
|
+
#ifdef HAVE_STDLIB_H
|
|
5
|
+
#include <stdlib.h>
|
|
6
|
+
#endif
|
|
7
|
+
#include <string.h>
|
|
8
|
+
|
|
9
|
+
#if SIZEOF_LONG == SIZEOF_VOIDP
|
|
10
|
+
typedef unsigned long st_data_t;
|
|
11
|
+
#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
|
|
12
|
+
typedef unsigned LONG_LONG st_data_t;
|
|
13
|
+
#else
|
|
14
|
+
# error ---->> sp_ar.c requires sizeof(void*) == sizeof(long) or sizeof(LONG_LONG) to be compiled. <<----
|
|
15
|
+
#endif
|
|
16
|
+
|
|
17
|
+
typedef unsigned int spar_index_t;
|
|
18
|
+
#define spar_STOP ST_STOP
|
|
19
|
+
#define spar_CONTINUE ST_CONTINUE
|
|
20
|
+
|
|
21
|
+
#define spar_EMPTY 0
|
|
22
|
+
|
|
23
|
+
typedef struct spar_entry {
|
|
24
|
+
spar_index_t next;
|
|
25
|
+
spar_index_t key;
|
|
26
|
+
st_data_t value;
|
|
27
|
+
} spar_entry;
|
|
28
|
+
|
|
29
|
+
typedef struct spar_table {
|
|
30
|
+
spar_index_t num_bins;
|
|
31
|
+
spar_index_t num_entries;
|
|
32
|
+
spar_index_t free_pos;
|
|
33
|
+
spar_entry *entries;
|
|
34
|
+
} spar_table;
|
|
35
|
+
|
|
36
|
+
#define spar_EMPTY_TABLE {0, 0, 0, 0};
|
|
37
|
+
static void spar_init_table(spar_table *, spar_index_t);
|
|
38
|
+
static spar_table *spar_new_table();
|
|
39
|
+
static int spar_insert(spar_table *, spar_index_t, st_data_t);
|
|
40
|
+
static int spar_lookup(spar_table *, spar_index_t, st_data_t *);
|
|
41
|
+
static int spar_delete(spar_table *, spar_index_t, st_data_t *);
|
|
42
|
+
static void spar_clear(spar_table *);
|
|
43
|
+
static void spar_free_table(spar_table *);
|
|
44
|
+
static size_t spar_memsize(const spar_table *);
|
|
45
|
+
static void spar_copy_to(spar_table*, spar_table*);
|
|
46
|
+
typedef int (*spar_iter_func)(spar_index_t key, st_data_t val, st_data_t arg);
|
|
47
|
+
|
|
48
|
+
#define SPAR_FOREACH_START_I(table, entry) do { \
|
|
49
|
+
spar_table *T##entry = (table); \
|
|
50
|
+
spar_index_t K##entry; \
|
|
51
|
+
for(K##entry = 0; K##entry < T##entry->num_bins; K##entry++) { \
|
|
52
|
+
spar_entry *entry = T##entry->entries + K##entry; \
|
|
53
|
+
if (entry->next != spar_EMPTY) { \
|
|
54
|
+
st_data_t value = entry->value
|
|
55
|
+
#define SPAR_FOREACH_END() } } } while(0)
|
|
56
|
+
|
|
57
|
+
#define SPAR_FOREACH_START(table) SPAR_FOREACH_START_I(table, entry)
|
|
58
|
+
|
|
59
|
+
#define malloc xmalloc
|
|
60
|
+
#define calloc xcalloc
|
|
61
|
+
#define realloc xrealloc
|
|
62
|
+
#define free xfree
|
|
63
|
+
|
|
64
|
+
#define spar_table_alloc() (spar_table*)malloc(sizeof(spar_table))
|
|
65
|
+
#define spar_table_xalloc() (spar_table*)calloc(1, sizeof(spar_table))
|
|
66
|
+
#define spar_table_dealloc(table) free(table)
|
|
67
|
+
#define spar_entry_alloc(n) (spar_entry*)calloc((n), sizeof(spar_entry))
|
|
68
|
+
#define spar_entry_dealloc(entries) free(entries)
|
|
69
|
+
|
|
70
|
+
#define spar_LAST 1
|
|
71
|
+
#define spar_OFFSET 2
|
|
72
|
+
|
|
73
|
+
#define spar_MIN_SIZE 4
|
|
74
|
+
|
|
75
|
+
static void
|
|
76
|
+
spar_init_table(register spar_table *table, spar_index_t num_bins)
|
|
77
|
+
{
|
|
78
|
+
if (num_bins) {
|
|
79
|
+
table->num_entries = 0;
|
|
80
|
+
table->entries = spar_entry_alloc(num_bins);
|
|
81
|
+
table->num_bins = num_bins;
|
|
82
|
+
table->free_pos = num_bins;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
memset(table, 0, sizeof(spar_table));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
static spar_table*
|
|
90
|
+
spar_new_table()
|
|
91
|
+
{
|
|
92
|
+
spar_table* table = spar_table_alloc();
|
|
93
|
+
spar_init_table(table, 0);
|
|
94
|
+
return table;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
static inline spar_index_t
|
|
98
|
+
calc_pos(register spar_table* table, spar_index_t key)
|
|
99
|
+
{
|
|
100
|
+
/* this formula is empirical */
|
|
101
|
+
/* it has no good avalance, but works well in our case */
|
|
102
|
+
key ^= key >> 16;
|
|
103
|
+
key *= 0x445229;
|
|
104
|
+
return (key + (key >> 16)) % table->num_bins;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
static void
|
|
108
|
+
fix_empty(register spar_table* table)
|
|
109
|
+
{
|
|
110
|
+
while(--table->free_pos &&
|
|
111
|
+
table->entries[table->free_pos-1].next != spar_EMPTY);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
#define FLOOR_TO_4 ((~((spar_index_t)0)) << 2)
|
|
115
|
+
static spar_index_t
|
|
116
|
+
find_empty(register spar_table* table, register spar_index_t pos)
|
|
117
|
+
{
|
|
118
|
+
spar_index_t new_pos = table->free_pos-1;
|
|
119
|
+
spar_entry *entry;
|
|
120
|
+
pos &= FLOOR_TO_4;
|
|
121
|
+
entry = table->entries+pos;
|
|
122
|
+
|
|
123
|
+
if (entry->next == spar_EMPTY) { new_pos = pos; }
|
|
124
|
+
else if ((++entry)->next == spar_EMPTY) { new_pos = pos + 1; }
|
|
125
|
+
else if ((++entry)->next == spar_EMPTY) { new_pos = pos + 2; }
|
|
126
|
+
else if ((++entry)->next == spar_EMPTY) { new_pos = pos + 3; }
|
|
127
|
+
|
|
128
|
+
if (new_pos+1 == table->free_pos) fix_empty(table);
|
|
129
|
+
return new_pos;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
static void resize(register spar_table* table);
|
|
133
|
+
static int insert_into_chain(register spar_table*, register spar_index_t, st_data_t, spar_index_t pos);
|
|
134
|
+
static int insert_into_main(register spar_table*, spar_index_t, st_data_t, spar_index_t pos, spar_index_t prev_pos);
|
|
135
|
+
|
|
136
|
+
static int
|
|
137
|
+
spar_insert(register spar_table* table, register spar_index_t key, st_data_t value)
|
|
138
|
+
{
|
|
139
|
+
spar_index_t pos, main_pos;
|
|
140
|
+
register spar_entry *entry;
|
|
141
|
+
|
|
142
|
+
if (table->num_bins == 0) {
|
|
143
|
+
spar_init_table(table, spar_MIN_SIZE);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
pos = calc_pos(table, key);
|
|
147
|
+
entry = table->entries + pos;
|
|
148
|
+
|
|
149
|
+
if (entry->next == spar_EMPTY) {
|
|
150
|
+
entry->next = spar_LAST;
|
|
151
|
+
entry->key = key;
|
|
152
|
+
entry->value = value;
|
|
153
|
+
table->num_entries++;
|
|
154
|
+
if (pos+1 == table->free_pos) fix_empty(table);
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (entry->key == key) {
|
|
159
|
+
entry->value = value;
|
|
160
|
+
return 1;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (table->num_entries + (table->num_entries >> 2) > table->num_bins) {
|
|
164
|
+
resize(table);
|
|
165
|
+
return spar_insert(table, key, value);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
main_pos = calc_pos(table, entry->key);
|
|
169
|
+
if (main_pos == pos) {
|
|
170
|
+
return insert_into_chain(table, key, value, pos);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
if (!table->free_pos) {
|
|
174
|
+
resize(table);
|
|
175
|
+
return spar_insert(table, key, value);
|
|
176
|
+
}
|
|
177
|
+
return insert_into_main(table, key, value, pos, main_pos);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
static int
|
|
182
|
+
insert_into_chain(register spar_table* table, register spar_index_t key, st_data_t value, spar_index_t pos)
|
|
183
|
+
{
|
|
184
|
+
spar_entry *entry = table->entries + pos, *new_entry;
|
|
185
|
+
spar_index_t new_pos;
|
|
186
|
+
|
|
187
|
+
while (entry->next != spar_LAST) {
|
|
188
|
+
pos = entry->next - spar_OFFSET;
|
|
189
|
+
entry = table->entries + pos;
|
|
190
|
+
if (entry->key == key) {
|
|
191
|
+
entry->value = value;
|
|
192
|
+
return 1;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (!table->free_pos) {
|
|
197
|
+
resize(table);
|
|
198
|
+
return spar_insert(table, key, value);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
new_pos = find_empty(table, pos);
|
|
202
|
+
new_entry = table->entries + new_pos;
|
|
203
|
+
entry->next = new_pos + spar_OFFSET;
|
|
204
|
+
|
|
205
|
+
new_entry->next = spar_LAST;
|
|
206
|
+
new_entry->key = key;
|
|
207
|
+
new_entry->value = value;
|
|
208
|
+
table->num_entries++;
|
|
209
|
+
return 0;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
static int
|
|
213
|
+
insert_into_main(register spar_table* table, spar_index_t key, st_data_t value, spar_index_t pos, spar_index_t prev_pos)
|
|
214
|
+
{
|
|
215
|
+
spar_entry *entry = table->entries + pos;
|
|
216
|
+
spar_index_t new_pos = find_empty(table, pos);
|
|
217
|
+
spar_entry *new_entry = table->entries + new_pos;
|
|
218
|
+
spar_index_t npos;
|
|
219
|
+
|
|
220
|
+
*new_entry = *entry;
|
|
221
|
+
|
|
222
|
+
while((npos = table->entries[prev_pos].next - spar_OFFSET) != pos) {
|
|
223
|
+
prev_pos = npos;
|
|
224
|
+
}
|
|
225
|
+
table->entries[prev_pos].next = new_pos + spar_OFFSET;
|
|
226
|
+
|
|
227
|
+
entry->next = spar_LAST;
|
|
228
|
+
entry->key = key;
|
|
229
|
+
entry->value = value;
|
|
230
|
+
table->num_entries++;
|
|
231
|
+
return 0;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
static spar_index_t
|
|
235
|
+
new_size(spar_index_t num_entries)
|
|
236
|
+
{
|
|
237
|
+
spar_index_t msb = num_entries;
|
|
238
|
+
msb |= msb >> 1;
|
|
239
|
+
msb |= msb >> 2;
|
|
240
|
+
msb |= msb >> 4;
|
|
241
|
+
msb |= msb >> 8;
|
|
242
|
+
msb |= msb >> 16;
|
|
243
|
+
msb = ((msb >> 4) + 1) << 3;
|
|
244
|
+
return (num_entries & (msb | (msb >> 1))) + (msb >> 1);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
static void
|
|
248
|
+
resize(register spar_table *table)
|
|
249
|
+
{
|
|
250
|
+
spar_table tmp_table;
|
|
251
|
+
spar_entry *entry;
|
|
252
|
+
spar_index_t i;
|
|
253
|
+
|
|
254
|
+
if (table->num_entries == 0) {
|
|
255
|
+
spar_entry_dealloc(table->entries);
|
|
256
|
+
memset(table, 0, sizeof(spar_table));
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
spar_init_table(&tmp_table, new_size(table->num_entries + (table->num_entries >> 2)));
|
|
261
|
+
entry = table->entries;
|
|
262
|
+
|
|
263
|
+
for(i = 0; i < table->num_bins; i++, entry++) {
|
|
264
|
+
if (entry->next != spar_EMPTY) {
|
|
265
|
+
spar_insert(&tmp_table, entry->key, entry->value);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
spar_entry_dealloc(table->entries);
|
|
269
|
+
*table = tmp_table;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
static int
|
|
273
|
+
spar_lookup(register spar_table *table, register spar_index_t key, st_data_t *value)
|
|
274
|
+
{
|
|
275
|
+
register spar_entry *entry;
|
|
276
|
+
|
|
277
|
+
if (table->num_entries == 0) return 0;
|
|
278
|
+
|
|
279
|
+
entry = table->entries + calc_pos(table, key);
|
|
280
|
+
if (entry->next == spar_EMPTY) return 0;
|
|
281
|
+
|
|
282
|
+
if (entry->key == key) goto found;
|
|
283
|
+
if (entry->next == spar_LAST) return 0;
|
|
284
|
+
|
|
285
|
+
entry = table->entries + (entry->next - spar_OFFSET);
|
|
286
|
+
if (entry->key == key) goto found;
|
|
287
|
+
|
|
288
|
+
while(entry->next != spar_LAST) {
|
|
289
|
+
entry = table->entries + (entry->next - spar_OFFSET);
|
|
290
|
+
if (entry->key == key) goto found;
|
|
291
|
+
}
|
|
292
|
+
return 0;
|
|
293
|
+
found:
|
|
294
|
+
if (value) *value = entry->value;
|
|
295
|
+
return 1;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
static void
|
|
299
|
+
spar_clear(spar_table *table)
|
|
300
|
+
{
|
|
301
|
+
spar_entry_dealloc(table->entries);
|
|
302
|
+
memset(table, 0, sizeof(spar_table));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
static void
|
|
306
|
+
spar_free_table(spar_table *table)
|
|
307
|
+
{
|
|
308
|
+
spar_entry_dealloc(table->entries);
|
|
309
|
+
spar_table_dealloc(table);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
static int
|
|
313
|
+
spar_delete(spar_table *table, spar_index_t key, st_data_t *value)
|
|
314
|
+
{
|
|
315
|
+
spar_index_t pos, prev_pos = ~0;
|
|
316
|
+
spar_entry *entry;
|
|
317
|
+
|
|
318
|
+
if (table->num_entries == 0) goto not_found;
|
|
319
|
+
|
|
320
|
+
pos = calc_pos(table, key);
|
|
321
|
+
entry = table->entries + pos;
|
|
322
|
+
|
|
323
|
+
if (entry->next == spar_EMPTY) goto not_found;
|
|
324
|
+
|
|
325
|
+
do {
|
|
326
|
+
if (entry->key == key) {
|
|
327
|
+
if (value) *value = entry->value;
|
|
328
|
+
if (entry->next != spar_LAST) {
|
|
329
|
+
spar_index_t npos = entry->next - spar_OFFSET;
|
|
330
|
+
*entry = table->entries[npos];
|
|
331
|
+
memset(table->entries + npos, 0, sizeof(spar_entry));
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
memset(table->entries + pos, 0, sizeof(spar_entry));
|
|
335
|
+
if (~prev_pos) {
|
|
336
|
+
table->entries[prev_pos].next = spar_LAST;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
table->num_entries--;
|
|
340
|
+
if (table->num_entries < table->num_bins / 4) {
|
|
341
|
+
resize(table);
|
|
342
|
+
}
|
|
343
|
+
return 1;
|
|
344
|
+
}
|
|
345
|
+
if (entry->next == spar_LAST) break;
|
|
346
|
+
prev_pos = pos;
|
|
347
|
+
pos = entry->next - spar_OFFSET;
|
|
348
|
+
entry = table->entries + pos;
|
|
349
|
+
} while(1);
|
|
350
|
+
|
|
351
|
+
not_found:
|
|
352
|
+
if (value) *value = 0;
|
|
353
|
+
return 0;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
static size_t
|
|
357
|
+
spar_memsize(const spar_table *table)
|
|
358
|
+
{
|
|
359
|
+
return sizeof(spar_table) + table->num_bins * sizeof(spar_entry);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
static void
|
|
363
|
+
spar_copy_to(spar_table *from, spar_table *to)
|
|
364
|
+
{
|
|
365
|
+
spar_entry_dealloc(to->entries);
|
|
366
|
+
*to = *from;
|
|
367
|
+
if (to->num_bins) {
|
|
368
|
+
to->entries = spar_entry_alloc(to->num_bins);
|
|
369
|
+
memcpy(to->entries, from->entries, from->num_bins*sizeof(spar_entry));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/******** Ruby Sparse Array binding ********/
|
|
374
|
+
|
|
375
|
+
static void
|
|
376
|
+
sparse_array_mark(void *p)
|
|
377
|
+
{
|
|
378
|
+
if (p) {
|
|
379
|
+
spar_table *ar = (spar_table*)p;
|
|
380
|
+
SPAR_FOREACH_START(ar);
|
|
381
|
+
rb_gc_mark((VALUE)value);
|
|
382
|
+
SPAR_FOREACH_END();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
static void
|
|
387
|
+
sparse_array_delete(void *p)
|
|
388
|
+
{
|
|
389
|
+
if (p) {
|
|
390
|
+
spar_free_table((spar_table*) p);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
static size_t
|
|
395
|
+
sparse_array_memsize(const void *p)
|
|
396
|
+
{
|
|
397
|
+
if (p) {
|
|
398
|
+
return spar_memsize((const spar_table*) p);
|
|
399
|
+
}
|
|
400
|
+
return 0;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
static const rb_data_type_t SparseArray_data_type = {
|
|
404
|
+
"SparseArrayC",
|
|
405
|
+
{sparse_array_mark, sparse_array_delete, sparse_array_memsize}
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
#define GetSparseArray(value, pointer) \
|
|
409
|
+
TypedData_Get_Struct((value), spar_table, &SparseArray_data_type, (pointer))
|
|
410
|
+
|
|
411
|
+
static VALUE
|
|
412
|
+
sparse_array_alloc(VALUE klass)
|
|
413
|
+
{
|
|
414
|
+
spar_table* table = spar_new_table();
|
|
415
|
+
return TypedData_Wrap_Struct(klass, &SparseArray_data_type, table);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
static VALUE
|
|
419
|
+
sparse_array_get(VALUE self, VALUE ri)
|
|
420
|
+
{
|
|
421
|
+
spar_table *table;
|
|
422
|
+
spar_index_t i = NUM2UINT(ri);
|
|
423
|
+
st_data_t res = Qnil;
|
|
424
|
+
GetSparseArray(self, table);
|
|
425
|
+
spar_lookup(table, i, &res);
|
|
426
|
+
return (VALUE)res;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
static VALUE
|
|
430
|
+
sparse_array_include(VALUE self, VALUE ri)
|
|
431
|
+
{
|
|
432
|
+
spar_table *table;
|
|
433
|
+
spar_index_t i = NUM2UINT(ri);
|
|
434
|
+
st_data_t res = Qnil;
|
|
435
|
+
GetSparseArray(self, table);
|
|
436
|
+
if (spar_lookup(table, i, &res))
|
|
437
|
+
return Qtrue;
|
|
438
|
+
else
|
|
439
|
+
return Qfalse;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
static VALUE
|
|
443
|
+
sparse_array_fetch(VALUE self, VALUE ri, VALUE def)
|
|
444
|
+
{
|
|
445
|
+
spar_table *table;
|
|
446
|
+
spar_index_t i = NUM2UINT(ri);
|
|
447
|
+
st_data_t res = Qnil;
|
|
448
|
+
GetSparseArray(self, table);
|
|
449
|
+
if (spar_lookup(table, i, &res))
|
|
450
|
+
return (VALUE)res;
|
|
451
|
+
else
|
|
452
|
+
return def;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
static VALUE
|
|
456
|
+
sparse_array_set(VALUE self, VALUE ri, VALUE val)
|
|
457
|
+
{
|
|
458
|
+
spar_table *table;
|
|
459
|
+
spar_index_t i = NUM2UINT(ri);
|
|
460
|
+
GetSparseArray(self, table);
|
|
461
|
+
spar_insert(table, i, val);
|
|
462
|
+
return val;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
static VALUE
|
|
466
|
+
sparse_array_del(VALUE self, VALUE ri)
|
|
467
|
+
{
|
|
468
|
+
spar_table *table;
|
|
469
|
+
spar_index_t i = NUM2UINT(ri);
|
|
470
|
+
st_data_t res = Qnil;
|
|
471
|
+
GetSparseArray(self, table);
|
|
472
|
+
if (spar_delete(table, i, &res))
|
|
473
|
+
return (VALUE)res;
|
|
474
|
+
return Qnil;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
static VALUE
|
|
478
|
+
sparse_array_size(VALUE self)
|
|
479
|
+
{
|
|
480
|
+
spar_table *table;
|
|
481
|
+
GetSparseArray(self, table);
|
|
482
|
+
return UINT2NUM(table->num_entries);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
static VALUE
|
|
486
|
+
sparse_array_empty_p(VALUE self)
|
|
487
|
+
{
|
|
488
|
+
spar_table *table;
|
|
489
|
+
GetSparseArray(self, table);
|
|
490
|
+
return table->num_entries ? Qfalse : Qtrue;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
static VALUE
|
|
494
|
+
sparse_array_clear(VALUE self)
|
|
495
|
+
{
|
|
496
|
+
spar_table *table;
|
|
497
|
+
GetSparseArray(self, table);
|
|
498
|
+
spar_clear(table);
|
|
499
|
+
return self;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
static VALUE
|
|
503
|
+
sparse_array_keys(VALUE self)
|
|
504
|
+
{
|
|
505
|
+
spar_table *table;
|
|
506
|
+
VALUE res;
|
|
507
|
+
GetSparseArray(self, table);
|
|
508
|
+
res = rb_ary_new2(table->num_entries);
|
|
509
|
+
SPAR_FOREACH_START(table);
|
|
510
|
+
(void)value;
|
|
511
|
+
rb_ary_push(res, UINT2NUM(entry->key));
|
|
512
|
+
SPAR_FOREACH_END();
|
|
513
|
+
return res;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
static VALUE
|
|
517
|
+
sparse_array_values(VALUE self)
|
|
518
|
+
{
|
|
519
|
+
spar_table *table;
|
|
520
|
+
VALUE res;
|
|
521
|
+
GetSparseArray(self, table);
|
|
522
|
+
res = rb_ary_new2(table->num_entries);
|
|
523
|
+
SPAR_FOREACH_START(table);
|
|
524
|
+
rb_ary_push(res, (VALUE)value);
|
|
525
|
+
SPAR_FOREACH_END();
|
|
526
|
+
return res;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
static VALUE
|
|
530
|
+
sparse_array_each(VALUE self)
|
|
531
|
+
{
|
|
532
|
+
spar_table *table;
|
|
533
|
+
VALUE keys;
|
|
534
|
+
long i, size;
|
|
535
|
+
VALUE *p;
|
|
536
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
537
|
+
GetSparseArray(self, table);
|
|
538
|
+
keys = sparse_array_keys(self);
|
|
539
|
+
size = RARRAY_LEN(keys);
|
|
540
|
+
p = RARRAY_PTR(keys);
|
|
541
|
+
for(i=0; i<size; i++) {
|
|
542
|
+
spar_index_t k = NUM2UINT(p[i]);
|
|
543
|
+
st_data_t v = Qnil;
|
|
544
|
+
if (spar_lookup(table, k, &v))
|
|
545
|
+
rb_yield(rb_assoc_new(p[i], (VALUE)v));
|
|
546
|
+
}
|
|
547
|
+
RB_GC_GUARD(keys);
|
|
548
|
+
return self;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
static VALUE
|
|
552
|
+
sparse_array_each_key(VALUE self)
|
|
553
|
+
{
|
|
554
|
+
spar_table *table;
|
|
555
|
+
VALUE keys;
|
|
556
|
+
long i, size;
|
|
557
|
+
VALUE *p;
|
|
558
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
559
|
+
GetSparseArray(self, table);
|
|
560
|
+
keys = sparse_array_keys(self);
|
|
561
|
+
size = RARRAY_LEN(keys);
|
|
562
|
+
p = RARRAY_PTR(keys);
|
|
563
|
+
for(i=0; i<size; i++) {
|
|
564
|
+
spar_index_t k = NUM2UINT(p[i]);
|
|
565
|
+
st_data_t v = Qnil;
|
|
566
|
+
if (spar_lookup(table, k, &v))
|
|
567
|
+
rb_yield(p[i]);
|
|
568
|
+
}
|
|
569
|
+
RB_GC_GUARD(keys);
|
|
570
|
+
return self;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
static VALUE
|
|
574
|
+
sparse_array_each_value(VALUE self)
|
|
575
|
+
{
|
|
576
|
+
spar_table *table;
|
|
577
|
+
VALUE keys;
|
|
578
|
+
long i, size;
|
|
579
|
+
VALUE *p;
|
|
580
|
+
RETURN_ENUMERATOR(self, 0, 0);
|
|
581
|
+
GetSparseArray(self, table);
|
|
582
|
+
keys = sparse_array_keys(self);
|
|
583
|
+
size = RARRAY_LEN(keys);
|
|
584
|
+
p = RARRAY_PTR(keys);
|
|
585
|
+
for(i=0; i<size; i++) {
|
|
586
|
+
spar_index_t k = NUM2UINT(p[i]);
|
|
587
|
+
st_data_t v = Qnil;
|
|
588
|
+
if (spar_lookup(table, k, &v))
|
|
589
|
+
rb_yield(v);
|
|
590
|
+
}
|
|
591
|
+
RB_GC_GUARD(keys);
|
|
592
|
+
return self;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
static VALUE
|
|
596
|
+
sparse_array_init_copy(VALUE self, VALUE copy)
|
|
597
|
+
{
|
|
598
|
+
spar_table *table;
|
|
599
|
+
spar_table *copied;
|
|
600
|
+
GetSparseArray(self, table);
|
|
601
|
+
GetSparseArray(copy, copied);
|
|
602
|
+
rb_obj_init_copy(self, copy);
|
|
603
|
+
spar_copy_to(table, copied);
|
|
604
|
+
return copy;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
static VALUE
|
|
608
|
+
sparse_array_inspect_rec(VALUE self, VALUE dummy, int recur)
|
|
609
|
+
{
|
|
610
|
+
VALUE str, str2;
|
|
611
|
+
spar_table *table;
|
|
612
|
+
GetSparseArray(self, table);
|
|
613
|
+
|
|
614
|
+
if (recur) return rb_usascii_str_new2("<SparseArray ...>");
|
|
615
|
+
str = rb_str_buf_new2("<SparseArray");
|
|
616
|
+
SPAR_FOREACH_START(table);
|
|
617
|
+
rb_str_catf(str, " %u=>", entry->key);
|
|
618
|
+
str2 = rb_inspect(value);
|
|
619
|
+
rb_str_buf_append(str, str2);
|
|
620
|
+
OBJ_INFECT(str, str2);
|
|
621
|
+
SPAR_FOREACH_END();
|
|
622
|
+
rb_str_buf_cat2(str, ">");
|
|
623
|
+
OBJ_INFECT(str, self);
|
|
624
|
+
|
|
625
|
+
return str;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
static VALUE
|
|
629
|
+
sparse_array_inspect(VALUE self)
|
|
630
|
+
{
|
|
631
|
+
spar_table *table;
|
|
632
|
+
GetSparseArray(self, table);
|
|
633
|
+
if (table->num_entries == 0)
|
|
634
|
+
return rb_usascii_str_new2("<SparseArray>");
|
|
635
|
+
return rb_exec_recursive(sparse_array_inspect_rec, self, 0);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
void
|
|
639
|
+
Init_sparse_array() {
|
|
640
|
+
VALUE cls_sparse_array = rb_define_class("SparseArray", rb_cObject);
|
|
641
|
+
rb_define_alloc_func(cls_sparse_array, sparse_array_alloc);
|
|
642
|
+
rb_define_method(cls_sparse_array, "[]", sparse_array_get, 1);
|
|
643
|
+
rb_define_method(cls_sparse_array, "fetch", sparse_array_fetch, 2);
|
|
644
|
+
rb_define_method(cls_sparse_array, "[]=", sparse_array_set, 2);
|
|
645
|
+
rb_define_method(cls_sparse_array, "delete", sparse_array_del, 1);
|
|
646
|
+
rb_define_method(cls_sparse_array, "clear", sparse_array_clear, 0);
|
|
647
|
+
rb_define_method(cls_sparse_array, "empty?", sparse_array_empty_p, 0);
|
|
648
|
+
rb_define_method(cls_sparse_array, "size", sparse_array_size, 1);
|
|
649
|
+
rb_define_method(cls_sparse_array, "count", sparse_array_size, 1);
|
|
650
|
+
rb_define_method(cls_sparse_array, "include?", sparse_array_include, 1);
|
|
651
|
+
rb_define_method(cls_sparse_array, "has_key?", sparse_array_include, 1);
|
|
652
|
+
rb_define_method(cls_sparse_array, "keys", sparse_array_keys, 0);
|
|
653
|
+
rb_define_method(cls_sparse_array, "values", sparse_array_values, 0);
|
|
654
|
+
rb_define_method(cls_sparse_array, "each", sparse_array_each, 0);
|
|
655
|
+
rb_define_method(cls_sparse_array, "each_pair", sparse_array_each, 0);
|
|
656
|
+
rb_define_method(cls_sparse_array, "each_key", sparse_array_each_key, 0);
|
|
657
|
+
rb_define_method(cls_sparse_array, "each_value", sparse_array_each_value, 0);
|
|
658
|
+
rb_define_method(cls_sparse_array, "inspect", sparse_array_inspect, 0);
|
|
659
|
+
rb_define_method(cls_sparse_array, "initialize_copy", sparse_array_init_copy, 1);
|
|
660
|
+
rb_include_module(cls_sparse_array, rb_mEnumerable);
|
|
661
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'sparse_array/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "sparse_array"
|
|
8
|
+
spec.version = SparseArray::VERSION
|
|
9
|
+
spec.authors = ["Sokolov Yura aka funny_falcon"]
|
|
10
|
+
spec.email = ["funny.falcon@gmail.com"]
|
|
11
|
+
spec.description = %q{Sparse Array - map from integers (0..2**32-1) to objects}
|
|
12
|
+
spec.summary = %q{lightweight map from integers to objects}
|
|
13
|
+
spec.homepage = "https://github.com/funny-falcon/ruby_sparse_array"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = %w{ext/extconf.rb ext/sp_ar.c lib/sparse_array/version.rb Gemfile LICENSE.txt sparse_array.gemspec}
|
|
17
|
+
spec.extensions = ["ext/extconf.rb"]
|
|
18
|
+
spec.require_paths = ["lib", "ext"]
|
|
19
|
+
|
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
|
21
|
+
spec.add_development_dependency "rake"
|
|
22
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: sparse_array
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Sokolov Yura aka funny_falcon
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2013-03-19 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
type: :development
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
requirements:
|
|
18
|
+
- - ~>
|
|
19
|
+
- !ruby/object:Gem::Version
|
|
20
|
+
version: '1.3'
|
|
21
|
+
none: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ~>
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.3'
|
|
27
|
+
none: false
|
|
28
|
+
prerelease: false
|
|
29
|
+
name: bundler
|
|
30
|
+
- !ruby/object:Gem::Dependency
|
|
31
|
+
type: :development
|
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
|
33
|
+
requirements:
|
|
34
|
+
- - ! '>='
|
|
35
|
+
- !ruby/object:Gem::Version
|
|
36
|
+
version: '0'
|
|
37
|
+
none: false
|
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ! '>='
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '0'
|
|
43
|
+
none: false
|
|
44
|
+
prerelease: false
|
|
45
|
+
name: rake
|
|
46
|
+
description: Sparse Array - map from integers (0..2**32-1) to objects
|
|
47
|
+
email:
|
|
48
|
+
- funny.falcon@gmail.com
|
|
49
|
+
executables: []
|
|
50
|
+
extensions:
|
|
51
|
+
- ext/extconf.rb
|
|
52
|
+
extra_rdoc_files: []
|
|
53
|
+
files:
|
|
54
|
+
- ext/extconf.rb
|
|
55
|
+
- ext/sp_ar.c
|
|
56
|
+
- lib/sparse_array/version.rb
|
|
57
|
+
- Gemfile
|
|
58
|
+
- LICENSE.txt
|
|
59
|
+
- sparse_array.gemspec
|
|
60
|
+
homepage: https://github.com/funny-falcon/ruby_sparse_array
|
|
61
|
+
licenses:
|
|
62
|
+
- MIT
|
|
63
|
+
post_install_message:
|
|
64
|
+
rdoc_options: []
|
|
65
|
+
require_paths:
|
|
66
|
+
- lib
|
|
67
|
+
- ext
|
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
69
|
+
requirements:
|
|
70
|
+
- - ! '>='
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: '0'
|
|
73
|
+
none: false
|
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
|
+
requirements:
|
|
76
|
+
- - ! '>='
|
|
77
|
+
- !ruby/object:Gem::Version
|
|
78
|
+
version: '0'
|
|
79
|
+
none: false
|
|
80
|
+
requirements: []
|
|
81
|
+
rubyforge_project:
|
|
82
|
+
rubygems_version: 1.8.25
|
|
83
|
+
signing_key:
|
|
84
|
+
specification_version: 3
|
|
85
|
+
summary: lightweight map from integers to objects
|
|
86
|
+
test_files: []
|