prometheus-client-mmap 0.7.0.beta39 → 0.7.0.beta40
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.
- checksums.yaml +4 -4
- data/ext/fast_mmaped_file/extconf.rb +1 -1
- data/ext/fast_mmaped_file/fast_mmaped_file.c +74 -148
- data/ext/fast_mmaped_file/file_aggregation.c +0 -0
- data/ext/fast_mmaped_file/file_format.h +13 -0
- data/ext/fast_mmaped_file/file_parsing.c +222 -0
- data/ext/fast_mmaped_file/file_parsing.h +23 -0
- data/ext/fast_mmaped_file/file_reading.c +93 -0
- data/ext/fast_mmaped_file/file_reading.h +30 -0
- data/ext/fast_mmaped_file/globals.h +13 -0
- data/ext/fast_mmaped_file/hashmap.c +692 -0
- data/ext/fast_mmaped_file/hashmap.h +266 -0
- data/ext/fast_mmaped_file/jsmn.c +313 -0
- data/ext/fast_mmaped_file/jsmn.h +76 -0
- data/ext/fast_mmaped_file/mmap.h +2 -0
- data/ext/fast_mmaped_file/rendering.c +92 -0
- data/ext/fast_mmaped_file/rendering.h +8 -0
- data/ext/fast_mmaped_file/utils.c +25 -0
- data/ext/fast_mmaped_file/utils.h +26 -0
- data/ext/fast_mmaped_file/value_access.c +160 -0
- data/ext/fast_mmaped_file/value_access.h +14 -0
- data/lib/fast_mmaped_file.bundle +0 -0
- data/lib/prometheus/client/formats/text.rb +4 -2
- data/lib/prometheus/client/helper/plain_file.rb +6 -3
- data/lib/prometheus/client/version.rb +1 -1
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f51d0d3d39230181b8a35905cac312c52dde03b
|
4
|
+
data.tar.gz: 1ec3f78ad1276fb225d73e31f1f0e91369c8afa2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf88cde2f108d7b3ade4ae808e0be40256acfa39cc8df26a3ce6ed7e023113314907f777afee1e28ac252293f46815b5bc898b9ab35485ccc61cc387b1c49934
|
7
|
+
data.tar.gz: 2d47f29dba886af6f5d3d462754712074e3c7d3e11c1072f399fd9621055299ec16dd5baa5e18c765220e013171d8b21596c7f96593ceb94479773821cd1b3cc
|
@@ -1,175 +1,101 @@
|
|
1
1
|
#include <ruby.h>
|
2
2
|
#include <ruby/intern.h>
|
3
|
-
#include <sys/mman.h>
|
4
|
-
#include <unistd.h>
|
5
|
-
#include <fcntl.h>
|
6
3
|
#include <errno.h>
|
7
4
|
|
8
|
-
#include
|
5
|
+
#include <utils.h>
|
6
|
+
#include <value_access.h>
|
7
|
+
#include <globals.h>
|
9
8
|
|
10
|
-
VALUE MMAPED_FILE = Qnil;
|
11
|
-
|
12
|
-
#define START_POSITION 8
|
13
|
-
#define INITIAL_SIZE (2 * sizeof(int32_t))
|
14
|
-
|
15
|
-
int open_and_extend_file(mm_ipc *i_mm, size_t len) {
|
16
|
-
int fd;
|
17
|
-
|
18
|
-
if ((fd = open(i_mm->t->path, i_mm->t->smode)) == -1) {
|
19
|
-
rb_raise(rb_eArgError, "Can't open %s", i_mm->t->path);
|
20
|
-
}
|
21
|
-
|
22
|
-
if (lseek(fd, len - i_mm->t->len - 1, SEEK_END) == -1) {
|
23
|
-
close(fd);
|
24
|
-
rb_raise(rb_eIOError, "Can't lseek %zu", len - i_mm->t->len - 1);
|
25
|
-
}
|
26
|
-
|
27
|
-
if (write(fd, "\000", 1) != 1) {
|
28
|
-
close(fd);
|
29
|
-
rb_raise(rb_eIOError, "Can't extend %s", i_mm->t->path);
|
30
|
-
}
|
31
|
-
|
32
|
-
return fd;
|
33
|
-
}
|
34
|
-
|
35
|
-
void expand(mm_ipc *i_mm, size_t len) {
|
36
|
-
if (len < i_mm->t->len) {
|
37
|
-
rb_raise(rb_eArgError, "Can't reduce the size of mmap");
|
38
|
-
}
|
39
|
-
|
40
|
-
if (munmap(i_mm->t->addr, i_mm->t->len)) {
|
41
|
-
rb_raise(rb_eArgError, "munmap failed");
|
42
|
-
}
|
43
|
-
|
44
|
-
int fd = open_and_extend_file(i_mm, len);
|
45
|
-
|
46
|
-
i_mm->t->addr = mmap(0, len, i_mm->t->pmode, i_mm->t->vscope, fd, i_mm->t->offset);
|
47
|
-
|
48
|
-
if (i_mm->t->addr == MAP_FAILED) {
|
49
|
-
close(fd);
|
50
|
-
rb_raise(rb_eArgError, "mmap failed");
|
51
|
-
}
|
52
|
-
|
53
|
-
if (close(fd) == -1) {
|
54
|
-
rb_raise(rb_eArgError, "Can't close %s", i_mm->t->path);
|
55
|
-
}
|
56
|
-
|
57
|
-
if ((i_mm->t->flag & MM_LOCK) && mlock(i_mm->t->addr, len) == -1) {
|
58
|
-
rb_raise(rb_eArgError, "mlock(%d)", errno);
|
59
|
-
}
|
60
|
-
i_mm->t->len = len;
|
61
|
-
i_mm->t->real = len;
|
62
|
-
}
|
63
|
-
|
64
|
-
inline uint32_t padding_length(uint32_t key_length) {
|
65
|
-
return 8 - (sizeof(uint32_t) + key_length) % 8; // padding | 8 byte aligned
|
66
|
-
}
|
67
|
-
|
68
|
-
void save_entry(mm_ipc *i_mm, uint32_t offset, VALUE key, VALUE value){
|
69
|
-
uint32_t key_length = (uint32_t)RSTRING_LEN(key);
|
70
|
-
|
71
|
-
char *pos = (char *)i_mm->t->addr + offset;
|
72
9
|
|
73
|
-
|
74
|
-
|
10
|
+
#include <jsmn.h>
|
11
|
+
#include <hashmap.h>
|
12
|
+
#include <file_reading.h>
|
13
|
+
#include <file_parsing.h>
|
14
|
+
#include <rendering.h>
|
75
15
|
|
76
|
-
|
77
|
-
pos += key_length;
|
78
|
-
|
79
|
-
memset(pos, ' ', padding_length(key_length));
|
80
|
-
pos += padding_length(key_length);
|
81
|
-
|
82
|
-
double val = NUM2DBL(value);
|
83
|
-
memcpy(pos, &val, sizeof(double));
|
84
|
-
}
|
85
|
-
|
86
|
-
inline uint32_t load_used(mm_ipc *i_mm) {
|
87
|
-
uint32_t used = *((uint32_t *)i_mm->t->addr);
|
88
|
-
|
89
|
-
if (used == 0) {
|
90
|
-
used = START_POSITION;
|
91
|
-
}
|
92
|
-
return used;
|
93
|
-
}
|
94
|
-
|
95
|
-
inline void save_used(mm_ipc *i_mm, uint32_t used) {
|
96
|
-
*((uint32_t *)i_mm->t->addr) = used;
|
97
|
-
}
|
98
|
-
|
99
|
-
VALUE method_load_used(VALUE self) {
|
100
|
-
mm_ipc *i_mm;
|
101
|
-
|
102
|
-
GET_MMAP(self, i_mm, MM_MODIFY);
|
103
|
-
return UINT2NUM(load_used(i_mm));
|
104
|
-
}
|
105
|
-
|
106
|
-
VALUE method_save_used(VALUE self, VALUE value) {
|
107
|
-
Check_Type(value, T_FIXNUM);
|
108
|
-
mm_ipc *i_mm;
|
109
|
-
|
110
|
-
GET_MMAP(self, i_mm, MM_MODIFY);
|
16
|
+
VALUE MMAPED_FILE = Qnil;
|
111
17
|
|
112
|
-
|
113
|
-
|
18
|
+
ID sym_min;
|
19
|
+
ID sym_max;
|
20
|
+
ID sym_livesum;
|
21
|
+
ID sym_gauge;
|
22
|
+
ID sym_pid;
|
23
|
+
ID sym_samples;
|
24
|
+
ID sym_key_name;
|
25
|
+
|
26
|
+
int aggregate_files(struct hashmap *map, VALUE list_of_files){
|
27
|
+
buffer_t reading_buffer = (buffer_t) { .buffer = NULL };
|
28
|
+
for(int i =0; i< RARRAY_LEN(list_of_files); i++){
|
29
|
+
VALUE params = RARRAY_PTR(list_of_files)[i];
|
30
|
+
file_t file;
|
31
|
+
|
32
|
+
if (!file_open_from_params(&file, params)) {
|
33
|
+
buffer_dispose(&reading_buffer);
|
34
|
+
return 0;
|
35
|
+
}
|
36
|
+
|
37
|
+
if (!read_from_file(&file, &reading_buffer)){
|
38
|
+
buffer_dispose(&reading_buffer);
|
39
|
+
file_close(&file);
|
40
|
+
return 0;
|
41
|
+
}
|
42
|
+
|
43
|
+
if (!process_buffer(&file, &reading_buffer, map)){
|
44
|
+
buffer_dispose(&reading_buffer);
|
45
|
+
file_close(&file);
|
46
|
+
return 0;
|
47
|
+
}
|
48
|
+
|
49
|
+
if (!file_close(&file)){
|
50
|
+
buffer_dispose(&reading_buffer);
|
51
|
+
return 0;
|
52
|
+
}
|
114
53
|
}
|
115
54
|
|
116
|
-
|
117
|
-
return
|
55
|
+
buffer_dispose(&reading_buffer);
|
56
|
+
return 1;
|
118
57
|
}
|
119
58
|
|
120
|
-
VALUE
|
121
|
-
|
122
|
-
|
59
|
+
VALUE method_to_metrics(VALUE UNUSED(self), VALUE file_list){
|
60
|
+
struct hashmap map;
|
61
|
+
hashmap_setup(&map);
|
123
62
|
|
124
|
-
|
125
|
-
|
126
|
-
|
63
|
+
if (!aggregate_files(&map, file_list)){ // all entries in map are now copies that need to be disposed
|
64
|
+
hashmap_destroy(&map);
|
65
|
+
raise_last_exception();
|
66
|
+
return Qnil;
|
127
67
|
}
|
128
68
|
|
129
|
-
|
130
|
-
GET_MMAP(self, i_mm, MM_MODIFY);
|
69
|
+
entry_struct **sorted_entries;
|
131
70
|
|
132
|
-
if (
|
133
|
-
|
134
|
-
|
71
|
+
if (!sort_map_entries(&map, &sorted_entries)){
|
72
|
+
entries_destroy(&map);
|
73
|
+
hashmap_destroy(&map);
|
135
74
|
|
136
|
-
|
137
|
-
|
75
|
+
raise_last_exception();
|
76
|
+
return Qnil;
|
138
77
|
}
|
139
78
|
|
140
|
-
|
141
|
-
uint32_t value_offset = sizeof(uint32_t) + key_length + padding_length(key_length);
|
142
|
-
uint32_t entry_length = value_offset + sizeof(double);
|
143
|
-
|
144
|
-
uint32_t used = load_used(i_mm);
|
145
|
-
while (i_mm->t->len < (used + entry_length)) {
|
146
|
-
expand(i_mm, i_mm->t->len * 2);
|
147
|
-
}
|
79
|
+
VALUE rv = entries_to_string(sorted_entries, hashmap_size(&map));
|
148
80
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
VALUE method_get_double(VALUE self, VALUE index) {
|
155
|
-
mm_ipc *i_mm;
|
156
|
-
GET_MMAP(self, i_mm, MM_MODIFY);
|
157
|
-
|
158
|
-
Check_Type(index, T_FIXNUM);
|
159
|
-
size_t idx = NUM2UINT(index);
|
160
|
-
|
161
|
-
if ((i_mm->t->real + sizeof(double)) <= idx) {
|
162
|
-
rb_raise(rb_eIndexError, "index %zu out of string", idx);
|
163
|
-
}
|
164
|
-
|
165
|
-
double tmp;
|
166
|
-
|
167
|
-
memcpy(&tmp, (char *)i_mm->t->addr + idx, sizeof(double));
|
168
|
-
return DBL2NUM(tmp);
|
81
|
+
free(sorted_entries);
|
82
|
+
entries_destroy(&map);
|
83
|
+
hashmap_destroy(&map);
|
84
|
+
return rv;
|
169
85
|
}
|
170
86
|
|
171
87
|
void Init_fast_mmaped_file() {
|
88
|
+
sym_gauge = rb_intern("gauge");
|
89
|
+
sym_min = rb_intern("min");
|
90
|
+
sym_max = rb_intern("max");
|
91
|
+
sym_livesum = rb_intern("livesum");
|
92
|
+
sym_pid = rb_intern("pid");
|
93
|
+
sym_samples = rb_intern("samples");
|
94
|
+
sym_key_name = rb_intern("__key_name");
|
95
|
+
|
172
96
|
MMAPED_FILE = rb_define_module("FastMmapedFile");
|
97
|
+
rb_define_singleton_method(MMAPED_FILE, "to_metrics", method_to_metrics, 1);
|
98
|
+
|
173
99
|
rb_define_method(MMAPED_FILE, "get_double", method_get_double, 1);
|
174
100
|
rb_define_method(MMAPED_FILE, "used", method_load_used, 0);
|
175
101
|
rb_define_method(MMAPED_FILE, "used=", method_save_used, 1);
|
File without changes
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#ifndef FILE_FORMAT_H
|
2
|
+
#define FILE_FORMAT_H
|
3
|
+
|
4
|
+
#include <stdint.h>
|
5
|
+
|
6
|
+
#define START_POSITION 8
|
7
|
+
#define INITIAL_SIZE (2 * sizeof(int32_t))
|
8
|
+
|
9
|
+
inline uint32_t padding_length(uint32_t key_length) {
|
10
|
+
return 8 - (sizeof(uint32_t) + key_length) % 8; // padding | 8 byte aligned
|
11
|
+
}
|
12
|
+
|
13
|
+
#endif
|
@@ -0,0 +1,222 @@
|
|
1
|
+
#include <hashmap.h>
|
2
|
+
#include <file_format.h>
|
3
|
+
#include <globals.h>
|
4
|
+
#include <utils.h>
|
5
|
+
#include <jsmn.h>
|
6
|
+
#include "file_parsing.h"
|
7
|
+
|
8
|
+
//ID sym_gauge;
|
9
|
+
//ID sym_min;
|
10
|
+
//ID sym_max;
|
11
|
+
//ID sym_livesum;
|
12
|
+
|
13
|
+
HASHMAP_FUNCS_CREATE(entry, const entry_struct, entry_struct)
|
14
|
+
|
15
|
+
typedef int (*compare_fn) (const void *a, const void *b);
|
16
|
+
|
17
|
+
inline int is_pid_significant(const entry_struct *a){
|
18
|
+
ID mp = a->multiprocess_mode;
|
19
|
+
return a->type == sym_gauge && !(mp == sym_min || mp == sym_max || mp == sym_livesum);
|
20
|
+
}
|
21
|
+
|
22
|
+
size_t hashmap_hash_entry(const entry_struct *entry){
|
23
|
+
size_t hash = 0;
|
24
|
+
|
25
|
+
for (size_t i = 0; i < entry->json_size; i++) {
|
26
|
+
hash += *(const char *)(entry->json + i);
|
27
|
+
hash += (hash << 10);
|
28
|
+
hash ^= (hash >> 6);
|
29
|
+
}
|
30
|
+
hash += (hash << 3);
|
31
|
+
hash ^= (hash >> 11);
|
32
|
+
hash += (hash << 15);
|
33
|
+
return hash;
|
34
|
+
}
|
35
|
+
|
36
|
+
int hashmap_compare_entry(const entry_struct *a, const entry_struct *b)
|
37
|
+
{
|
38
|
+
if (a->json_size != b->json_size){
|
39
|
+
return -1;
|
40
|
+
}
|
41
|
+
|
42
|
+
if (is_pid_significant(a) && (rb_str_equal(a->pid, b->pid) == Qfalse)){ //OPTIONAL TODO: optimize rb out
|
43
|
+
return -1;
|
44
|
+
}
|
45
|
+
|
46
|
+
return strncmp(a->json, b->json, a->json_size);
|
47
|
+
}
|
48
|
+
|
49
|
+
entry_struct *copy_entry(const entry_struct *entry){
|
50
|
+
entry_struct *copied = (entry_struct *)malloc(sizeof(entry_struct));
|
51
|
+
if (copied == NULL) {
|
52
|
+
return NULL;
|
53
|
+
}
|
54
|
+
memcpy(copied, entry, sizeof(entry_struct));
|
55
|
+
|
56
|
+
copied->json = malloc(entry->json_size);
|
57
|
+
if (copied->json == NULL){
|
58
|
+
free(copied);
|
59
|
+
return NULL;
|
60
|
+
}
|
61
|
+
|
62
|
+
memcpy(copied->json, entry->json, entry->json_size);
|
63
|
+
|
64
|
+
return copied;
|
65
|
+
}
|
66
|
+
|
67
|
+
void entry_free(entry_struct *entry){
|
68
|
+
free(entry->json);
|
69
|
+
free(entry);
|
70
|
+
}
|
71
|
+
|
72
|
+
void merge_entry(entry_struct *found, const entry_struct *entry){
|
73
|
+
if (entry->type == sym_gauge){
|
74
|
+
if (entry->multiprocess_mode == sym_min){
|
75
|
+
found->value = min(found->value, entry->value);
|
76
|
+
} else if (entry->multiprocess_mode == sym_max) {
|
77
|
+
found->value = max(found->value, entry->value);
|
78
|
+
} else if (entry->multiprocess_mode == sym_livesum) {
|
79
|
+
found->value += entry->value;
|
80
|
+
} else {
|
81
|
+
found->value += entry->value; //TODO: this shouldn't happen
|
82
|
+
}
|
83
|
+
} else {
|
84
|
+
found->value += entry->value;
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
int process_entry(struct hashmap *map, const entry_struct *entry){
|
89
|
+
entry_struct *found = entry_hashmap_get(map, entry);
|
90
|
+
if (found){
|
91
|
+
merge_entry(found, entry);
|
92
|
+
} else {
|
93
|
+
entry_struct *copy = copy_entry(entry);
|
94
|
+
if (copy == NULL) {
|
95
|
+
save_exception(rb_eNoMemError, "Failed copying metrics entry");
|
96
|
+
return 0;
|
97
|
+
}
|
98
|
+
entry_hashmap_put(map, copy, copy); // use the hashmap like hashset actually
|
99
|
+
}
|
100
|
+
return 1;
|
101
|
+
}
|
102
|
+
|
103
|
+
inline entry_struct entry_new(char* json, size_t json_size, double value, uint32_t pid){
|
104
|
+
return (entry_struct) { .json = json, .json_size = json_size, .pid = pid, .value = value, .name_len = 0 };
|
105
|
+
}
|
106
|
+
|
107
|
+
void add_parsed_name(entry_struct *entry){
|
108
|
+
jsmn_parser parser;
|
109
|
+
jsmn_init(&parser);
|
110
|
+
|
111
|
+
jsmntok_t t[2];
|
112
|
+
int r = jsmn_parse(&parser, entry->json, entry->json_size, t, 2);
|
113
|
+
|
114
|
+
if (r < 1) {
|
115
|
+
//TODO: better handle this case
|
116
|
+
}
|
117
|
+
|
118
|
+
entry->name = entry->json + t[1].start;
|
119
|
+
entry->name_len = t[1].end - t[1].start;
|
120
|
+
}
|
121
|
+
|
122
|
+
int entry_lexical_comparator(const entry_struct **a, const entry_struct **b){
|
123
|
+
size_t min_length = min((*a)->json_size, (*b)->json_size);
|
124
|
+
return strncmp((*a)->json, (*b)->json, min_length);
|
125
|
+
}
|
126
|
+
|
127
|
+
// public functions
|
128
|
+
|
129
|
+
void hashmap_setup(struct hashmap *map){
|
130
|
+
hashmap_init(map, (size_t (*)(const void *))hashmap_hash_entry, (int (*)(const void *, const void *))hashmap_compare_entry, 1000);
|
131
|
+
}
|
132
|
+
|
133
|
+
void entries_destroy(struct hashmap *map){
|
134
|
+
struct hashmap_iter *iter;
|
135
|
+
for (iter = hashmap_iter(map); iter; iter = hashmap_iter_next(map, iter)) {
|
136
|
+
entry_struct *entry = (entry_struct *)entry_hashmap_iter_get_key(iter);
|
137
|
+
entry_free(entry);
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
int process_buffer(file_t *file_info, buffer_t *source, struct hashmap *map){
|
142
|
+
if (source->size < START_POSITION) {
|
143
|
+
// nothing to read
|
144
|
+
return 1;
|
145
|
+
}
|
146
|
+
uint32_t used;
|
147
|
+
memcpy(&used, source->buffer, sizeof(uint32_t));
|
148
|
+
|
149
|
+
if (used > source->size){
|
150
|
+
//TODO: add custom parsing error
|
151
|
+
save_exception(rb_eRuntimeError, "source file %s corrupted, used %u > file size %u",
|
152
|
+
file_info->path, used, source->size);
|
153
|
+
return 0;
|
154
|
+
}
|
155
|
+
|
156
|
+
uint32_t pos = START_POSITION;
|
157
|
+
while (pos + sizeof(uint32_t) < used) {
|
158
|
+
uint32_t encoded_len;
|
159
|
+
memcpy(&encoded_len, source->buffer + pos, sizeof(uint32_t));
|
160
|
+
pos += sizeof(uint32_t);
|
161
|
+
|
162
|
+
uint32_t value_offset = encoded_len + padding_length(encoded_len);
|
163
|
+
|
164
|
+
if (pos + value_offset + sizeof(double) > used) {
|
165
|
+
//TODO: add custom parsing error
|
166
|
+
save_exception(rb_eRuntimeError, "source file %s corrupted, used %u < stored data length %u",
|
167
|
+
file_info->path, used, pos + value_offset + sizeof(double));
|
168
|
+
return 0;
|
169
|
+
}
|
170
|
+
|
171
|
+
entry_struct entry = entry_new(source->buffer + pos, encoded_len, 0, 0.0);
|
172
|
+
|
173
|
+
entry.multiprocess_mode = file_info->multiprocess_mode;
|
174
|
+
entry.pid = file_info->pid;
|
175
|
+
entry.type = file_info->type;
|
176
|
+
|
177
|
+
*(entry.json + encoded_len) = '\0'; // TODO: NONO
|
178
|
+
|
179
|
+
pos += value_offset;
|
180
|
+
memcpy(&entry.value, source->buffer + pos, sizeof(double)); //too
|
181
|
+
|
182
|
+
if (!process_entry(map, &entry)) {
|
183
|
+
entries_destroy(map);
|
184
|
+
return 0;
|
185
|
+
}
|
186
|
+
|
187
|
+
pos += sizeof(double);
|
188
|
+
}
|
189
|
+
return 1;
|
190
|
+
}
|
191
|
+
|
192
|
+
int sort_map_entries(const struct hashmap *map, entry_struct ***sorted_entries){
|
193
|
+
size_t num = hashmap_size(map);
|
194
|
+
|
195
|
+
size_t list_size = num * sizeof(entry_struct *);
|
196
|
+
entry_struct **list = malloc(list_size);
|
197
|
+
|
198
|
+
if (list == NULL){
|
199
|
+
save_exception(rb_eNoMemError, "Couldn't allocate %zu memory", list_size);
|
200
|
+
return 0;
|
201
|
+
}
|
202
|
+
|
203
|
+
size_t cnt = 0;
|
204
|
+
struct hashmap_iter *iter;
|
205
|
+
for (iter = hashmap_iter(map); iter; iter = hashmap_iter_next(map, iter)) {
|
206
|
+
entry_struct *entry = (entry_struct *)entry_hashmap_iter_get_key(iter);
|
207
|
+
add_parsed_name(entry);
|
208
|
+
|
209
|
+
list[cnt] = entry;
|
210
|
+
cnt++;
|
211
|
+
}
|
212
|
+
if (cnt != num){
|
213
|
+
save_exception(rb_eRuntimeError, "Processed entries %zu != map entries %zu", cnt, num);
|
214
|
+
free(list);
|
215
|
+
return 0;
|
216
|
+
}
|
217
|
+
|
218
|
+
qsort(list, cnt, sizeof(entry_struct *), (compare_fn)&entry_lexical_comparator);
|
219
|
+
*sorted_entries = list;
|
220
|
+
return 1;
|
221
|
+
}
|
222
|
+
|