hash_unnest 1.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c988aea31df14ef50afcaf73818f844a0ab61aca55fcf0614425a95441a6e810
4
+ data.tar.gz: 0f47f4110f2ee9a2ba2bd1133d8d55ec0c7c44135b569abc3efae44f6a18790f
5
+ SHA512:
6
+ metadata.gz: d75dfe4e4d93a2ba794d0b0d4cfac7120aca446372063af6e012dee0ffcfcfd6a8ece663394359d4b273dac3ef74f7f039ac0a4b28d80767fa0c1264047b3bac
7
+ data.tar.gz: 98252c49b5744768268406c302441eb3654daf8c120cfb9d8ad605df97d22176591a20000616ec46afca8b933517a17bbcf565f15c90cdc5719a16af35438dc6
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 HouseTrip Ltd.
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/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # hash-unnest
2
+
3
+ Un-nests hashes, ie transforms:
4
+
5
+ { a: { b: 1 }, c: 2 }
6
+
7
+ into
8
+
9
+ { a.b: 1, c: 2 }
10
+
11
+ Keys in the output hash will be sorted in lexicographical order.
12
+
13
+ ## Installation
14
+
15
+ gem install hash_unnest
16
+
17
+ ## Usage
18
+
19
+ ```ruby
20
+ require 'hash_unnest'
21
+ Hash.include(HashUnnest)
22
+
23
+ h = { a: { b: 1 }, c: 2 }
24
+ h.unnest
25
+ # => { a.b: 1, c: 2 }
26
+ ```
@@ -0,0 +1,12 @@
1
+ require 'mkmf'
2
+
3
+ PLATFORM = `uname`.strip.upcase
4
+ SHARED_FLAGS = "--std=c99 -Wall -Wextra -Werror"
5
+
6
+ # production
7
+ $CFLAGS += " #{SHARED_FLAGS} -Os"
8
+
9
+ # development
10
+ # $CFLAGS += " #{SHARED_FLAGS} -O0 -g -DDEBUG"
11
+
12
+ create_makefile('hash_unnest/hash_unnest')
@@ -0,0 +1,167 @@
1
+ #include <ruby.h>
2
+ #include <assert.h>
3
+ #include "hash_unnest.h"
4
+
5
+ // forward declarataions of closures
6
+ static int hash_unnest_size_closure(VALUE key, VALUE val, VALUE in);
7
+ static int hash_unnest_closure(VALUE key, VALUE val, VALUE in);
8
+
9
+ static VALUE eHashUnnestModule = Qnil;
10
+
11
+ // a (sortable) key-value entry
12
+ typedef struct {
13
+ VALUE key;
14
+ VALUE value;
15
+ } hn_record_t;
16
+
17
+ /******************************************************************************/
18
+
19
+ // Unnesting callback:
20
+ //
21
+ // - When `val` is anything but a hash, just add it to the `in[1]` buffer,
22
+ // using the prefix in `in[0]`.
23
+ //
24
+ // Note that in[1] is a pointer to the current buffer pointer;
25
+ // so it sets the buffer cell, thenmoves the pointer to the next cell.
26
+ //
27
+ // - When `val` is a hash, call ourselves recursively, appending the
28
+ // current key to the prefix.
29
+ //
30
+ static int hash_unnest_closure(VALUE key, VALUE val, VALUE in)
31
+ {
32
+ VALUE prefix = rb_ary_entry(in, 0);
33
+ hn_record_t** output = (hn_record_t**) rb_ary_entry(in, 1);
34
+
35
+ #ifdef DEBUG
36
+ VALUE _str_key = rb_funcall(key, rb_intern("inspect"), 0);
37
+ VALUE _str_val = rb_funcall(val, rb_intern("inspect"), 0);
38
+ LOG("*** hash_unnest_closure (%s,%s,'%s')\n", StringValueCStr(_str_key), StringValueCStr(_str_val), StringValueCStr(prefix));
39
+ #endif
40
+
41
+ switch(TYPE(val)) {
42
+ case T_HASH:
43
+ {
44
+ VALUE new_prefix;
45
+ VALUE new_in;
46
+
47
+ new_prefix = rb_str_dup(prefix);
48
+ rb_str_append(new_prefix, key);
49
+ rb_str_cat2(new_prefix, ".");
50
+
51
+ new_in = rb_ary_new2(2);
52
+ rb_ary_store(new_in, 0, new_prefix);
53
+ rb_ary_store(new_in, 1, (VALUE) output);
54
+
55
+ rb_hash_foreach(val, hash_unnest_closure, new_in);
56
+ break;
57
+ }
58
+ default:
59
+ {
60
+ VALUE new_key = rb_str_dup(prefix);
61
+
62
+ rb_str_append(new_key, key);
63
+ (*output)->key = new_key;
64
+ (*output)->value = val;
65
+ *output = *output + 1;
66
+ }
67
+ }
68
+ return ST_CONTINUE;
69
+ }
70
+
71
+ /******************************************************************************/
72
+
73
+ // Recursively counts the number of leaves ("size") of a nested hash.
74
+ static int hash_unnest_size_closure(VALUE key, VALUE val, VALUE in)
75
+ {
76
+ switch(TYPE(val)) {
77
+ case T_HASH:
78
+ {
79
+ rb_hash_foreach(val, hash_unnest_size_closure, in);
80
+ break;
81
+ }
82
+ default:
83
+ {
84
+ int* size = (int*) in;
85
+ *size = *size + 1;
86
+ }
87
+ }
88
+ return ST_CONTINUE;
89
+ }
90
+
91
+ /******************************************************************************/
92
+
93
+ // Compares the `key`s in `hn_record_t`s, for sorting purposes.
94
+ int hn_record_compare(const void* a, const void* b) {
95
+ hn_record_t* record_a = (hn_record_t*) a;
96
+ hn_record_t* record_b = (hn_record_t*) b;
97
+
98
+ LOG("compare '%s' to '%s'\n", StringValueCStr(record_a->key), StringValueCStr(record_b->key));
99
+
100
+ // NOTE: use StringValue + memcmp if slow
101
+ return strcmp(
102
+ StringValueCStr(record_a->key),
103
+ StringValueCStr(record_b->key)
104
+ );
105
+ }
106
+
107
+ /******************************************************************************/
108
+
109
+ static VALUE hash_unnest_unnest(VALUE self)
110
+ {
111
+ VALUE result, in, prefix;
112
+ hn_record_t* buf;
113
+ hn_record_t* buf_ptr;
114
+ int size = 0;
115
+
116
+ #ifdef DEBUG
117
+ {
118
+ VALUE _str_self = rb_funcall(self, rb_intern("to_s"), 0);
119
+ LOG("*** hash_unnest_unnest (%s)\n", StringValueCStr(_str_self));
120
+ }
121
+ #endif
122
+
123
+ rb_hash_foreach(self, hash_unnest_size_closure, (VALUE) &size);
124
+ #ifdef DEBUG
125
+ {
126
+ VALUE _str_self = rb_funcall(self, rb_intern("to_s"), 0);
127
+ LOG("*** size(%s): %d\n", StringValueCStr(_str_self), size);
128
+ }
129
+ #endif
130
+
131
+ buf = (hn_record_t*) malloc(size * sizeof(hn_record_t));
132
+ buf_ptr = buf;
133
+
134
+ prefix = rb_str_new("", 0);
135
+
136
+ in = rb_ary_new2(2);
137
+ rb_ary_store(in, 0, prefix);
138
+ rb_ary_store(in, 1, (VALUE) &buf_ptr);
139
+
140
+ rb_hash_foreach(self, hash_unnest_closure, in);
141
+
142
+ qsort((void*) buf, size, sizeof(hn_record_t), hn_record_compare);
143
+
144
+ result = rb_hash_new();
145
+ for (int k = 0; k < size; ++k) {
146
+ hn_record_t entry = buf[k];
147
+
148
+ rb_hash_aset(result, entry.key, entry.value);
149
+ }
150
+
151
+ free(buf);
152
+
153
+ return result;
154
+ }
155
+
156
+ /******************************************************************************/
157
+
158
+ void Init_hash_unnest(void) {
159
+ LOG("*** Init_hash_unnest\n");
160
+
161
+ /* assume we haven't yet defined hash_unnest */
162
+ eHashUnnestModule = rb_define_module("HashUnnest");
163
+ assert(eHashUnnestModule != Qnil);
164
+
165
+ rb_define_method(eHashUnnestModule, "unnest_c", hash_unnest_unnest, 0);
166
+ return;
167
+ }
@@ -0,0 +1,18 @@
1
+ /*
2
+
3
+ hash_unnest.h --
4
+
5
+ Helper macros
6
+
7
+ */
8
+
9
+ #ifndef __HASH_UNNEST_H__
10
+ #define __HASH_UNNEST_H__ 1
11
+
12
+ #ifdef DEBUG
13
+ #define LOG(...) fprintf(stderr, __VA_ARGS__)
14
+ #else
15
+ #define LOG(...)
16
+ #endif
17
+
18
+ #endif
@@ -0,0 +1,7 @@
1
+ require 'hash_unnest/hash_unnest'
2
+
3
+ module HashUnnest
4
+ def unnest
5
+ unnest_c
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ module HashUnnest
2
+ VERSION = '1.0.0'.freeze
3
+ end
4
+
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hash_unnest
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Julien Letessier
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-04-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake-compiler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Fast hash unnesting
70
+ email:
71
+ - julien.letessier@gmail.com
72
+ executables: []
73
+ extensions:
74
+ - ext/hash_unnest/extconf.rb
75
+ extra_rdoc_files: []
76
+ files:
77
+ - LICENSE.txt
78
+ - README.md
79
+ - ext/hash_unnest/extconf.rb
80
+ - ext/hash_unnest/hash_unnest.c
81
+ - ext/hash_unnest/hash_unnest.h
82
+ - lib/hash_unnest.rb
83
+ - lib/hash_unnest/version.rb
84
+ homepage: http://github.com/mezis/hash_unnest
85
+ licenses: []
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 2.7.6
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Fast hash unnesting
107
+ test_files: []