prometheus-client-mmap 0.7.0.beta39 → 0.7.0.beta40
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|