trace_visualization 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -1
- data/Rakefile +11 -1
- data/lib/trace_visualization/assert.rb +5 -0
- data/lib/trace_visualization/data/lexeme.rb +59 -0
- data/lib/trace_visualization/lexeme_overlap_filter.rb +64 -0
- data/lib/trace_visualization/mapping.rb +219 -56
- data/lib/trace_visualization/preprocessor/Makefile +43 -0
- data/lib/trace_visualization/preprocessor/hashmap.c +93 -0
- data/lib/trace_visualization/preprocessor/hashmap.h +27 -0
- data/lib/trace_visualization/preprocessor/hashmap_test.cpp +90 -0
- data/lib/trace_visualization/preprocessor/lexeme.h +11 -0
- data/lib/trace_visualization/preprocessor/lexeme_table.c +50 -0
- data/lib/trace_visualization/preprocessor/lexeme_table.h +13 -0
- data/lib/trace_visualization/preprocessor/lexeme_table_cpp.h +61 -0
- data/lib/trace_visualization/preprocessor/parser_functions.c +42 -0
- data/lib/trace_visualization/preprocessor/parser_test.cpp +71 -0
- data/lib/trace_visualization/preprocessor/preprocessor.l +18 -0
- data/lib/trace_visualization/preprocessor/test_main.cpp +39 -0
- data/lib/trace_visualization/profile.rb +38 -0
- data/lib/trace_visualization/reorder.rb +22 -11
- data/lib/trace_visualization/repetitions_psy.rb +1 -1
- data/lib/trace_visualization/suffix_array.rb +4 -0
- data/lib/trace_visualization/utils.rb +38 -0
- data/lib/trace_visualization/version.rb +1 -1
- data/lib/trace_visualization/visualization/console_color_print.rb +4 -2
- data/lib/trace_visualization.rb +68 -4
- data/spec/bwt_spec.rb +29 -7
- data/spec/lexeme_overlap_filter_spec.rb +59 -0
- data/spec/longest_common_prefix_spec.rb +3 -3
- data/spec/mapping_spec.rb +80 -34
- data/spec/reorder_spec.rb +25 -7
- data/spec/repetitions_psy_spec.rb +5 -5
- data/spec/suffix_array_spec.rb +30 -8
- data/spec/utils_spec.rb +30 -0
- metadata +22 -3
- data/LICENSE.txt +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5779b63579f93699f091c22ada7121df9469fc7
|
4
|
+
data.tar.gz: 336cfdc696d598e2247d09b089ec2b426df18e58
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 583564ac99432f9a0b05e1a51550497ae2f18d30fad51a7443fd8f9e6930567a66cac481a48c9cd594483a35eccdb4eff852a644bc8ce896fe7bffa14c3562fa
|
7
|
+
data.tar.gz: 721cf7cdf88fe2ac0380f4eb9517ffda3e6846cba7d317e6c5f1cdfac453c288b40b2b7d674de2039a0a2e88aab0a2b33b89dcde0d379a3b0c674b59fb5bb688
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -1 +1,11 @@
|
|
1
|
-
require
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
|
3
|
+
desc 'Compile preprocessor'
|
4
|
+
task :install do
|
5
|
+
system('cd lib/trace_visualization/preprocessor; make install; make clean')
|
6
|
+
end
|
7
|
+
|
8
|
+
desc 'Spec all functionality of gem'
|
9
|
+
task :spec_all do
|
10
|
+
system('rspec spec/*')
|
11
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module TraceVisualization
|
2
|
+
module Data
|
3
|
+
class Lexeme
|
4
|
+
attr_accessor :name
|
5
|
+
attr_accessor :value
|
6
|
+
attr_accessor :int_value
|
7
|
+
|
8
|
+
# Length of preprocessed string (see LEXEME_REGEXP)
|
9
|
+
attr_accessor :lexeme_length
|
10
|
+
|
11
|
+
attr_accessor :ord
|
12
|
+
|
13
|
+
def initialize(name, value, int_value = -1)
|
14
|
+
@name, @value, @int_value = name, value, int_value
|
15
|
+
end
|
16
|
+
|
17
|
+
def length
|
18
|
+
@value.length
|
19
|
+
end
|
20
|
+
|
21
|
+
def <=>(anOther)
|
22
|
+
@ord <=> anOther.ord
|
23
|
+
end
|
24
|
+
|
25
|
+
def <(other)
|
26
|
+
@ord < other.ord
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_int
|
30
|
+
@ord
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_i
|
34
|
+
to_int
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_str
|
38
|
+
@value
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
to_str
|
43
|
+
end
|
44
|
+
|
45
|
+
def method_missing(name, *args, &blk)
|
46
|
+
raise "Missing method #{name}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class LexemePos
|
51
|
+
attr_accessor :lexeme
|
52
|
+
attr_accessor :pos
|
53
|
+
|
54
|
+
def initialize(lexeme, pos)
|
55
|
+
@lexeme, @pos = lexeme, pos
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module TraceVisualization
|
2
|
+
module LexemeOverlapFilter
|
3
|
+
|
4
|
+
def self.process(lexeme_poss)
|
5
|
+
lexeme_poss.sort! { |a, b| a.pos <=> b.pos }
|
6
|
+
left_bound = lexeme_poss.inject(0) do |left_bound, lexeme_pos|
|
7
|
+
[lexeme_pos.pos + lexeme_pos.lexeme.length, left_bound].max
|
8
|
+
end
|
9
|
+
|
10
|
+
idx, current, result = 0, [], []
|
11
|
+
|
12
|
+
for pos in 0 .. left_bound
|
13
|
+
|
14
|
+
i = 0
|
15
|
+
while i < current.size
|
16
|
+
lexeme_pos = current[i]
|
17
|
+
|
18
|
+
if lexeme_pos.pos + lexeme_pos.lexeme.length == pos
|
19
|
+
fl_delete_lexeme = false
|
20
|
+
|
21
|
+
if current.size == 1
|
22
|
+
result << lexeme_pos
|
23
|
+
fl_delete_lexeme = true
|
24
|
+
else
|
25
|
+
if is_longest_lexeme(current, lexeme_pos)
|
26
|
+
result << lexeme_pos
|
27
|
+
current = []
|
28
|
+
else
|
29
|
+
fl_delete_lexeme = true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
if fl_delete_lexeme
|
34
|
+
current.delete_at(i)
|
35
|
+
i -= 1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
i += 1
|
40
|
+
end
|
41
|
+
|
42
|
+
while idx < lexeme_poss.size && lexeme_poss[idx].pos == pos
|
43
|
+
current << lexeme_poss[idx]
|
44
|
+
idx += 1
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
result
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.is_longest_lexeme(lexeme_poss, lexeme_pos)
|
52
|
+
result = true
|
53
|
+
|
54
|
+
lexeme_poss.each do |other_lexeme_pos|
|
55
|
+
if lexeme_pos != other_lexeme_pos && lexeme_pos.lexeme.length < other_lexeme_pos.lexeme.length
|
56
|
+
result = false
|
57
|
+
break
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
result
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -1,72 +1,241 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
require 'time'
|
2
|
+
require 'ipaddr'
|
3
|
+
|
4
|
+
require 'trace_visualization/reorder'
|
5
|
+
require 'trace_visualization/lexeme_overlap_filter'
|
6
|
+
require 'trace_visualization/data/lexeme'
|
5
7
|
|
6
|
-
|
8
|
+
module TraceVisualization
|
9
|
+
class Mapping
|
7
10
|
|
8
|
-
|
11
|
+
attr_accessor :tokens
|
12
|
+
|
13
|
+
LEXEME_REGEXP = /\{LEXEME;(?<name>[a-zA-Z0-9]+);(?<source>[^;]+);(?<value>[0-9]+)\}/
|
9
14
|
|
10
|
-
|
11
|
-
|
15
|
+
DEFAULT_TOKENS = {
|
16
|
+
:ID => [
|
17
|
+
/(?<lexeme>\[\d{3,}\])/,
|
18
|
+
lambda { |source| source[1 ... -1].to_i }
|
12
19
|
],
|
13
|
-
|
14
|
-
|
15
|
-
/(?<
|
20
|
+
|
21
|
+
:IP => [
|
22
|
+
/(?<lexeme>(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))/,
|
23
|
+
lambda { |source| IPAddr.new(source).to_i }
|
16
24
|
],
|
25
|
+
|
26
|
+
:TIME => [
|
27
|
+
/(?<lexeme>\[\d{2} [a-zA-Z]{3} \d{4} \d{2}\:\d{2}\:\d{2}\])/,
|
28
|
+
lambda { |source| Time.parse(source[1 ... -1]).to_i }
|
29
|
+
]
|
30
|
+
}
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
# hash map: lexeme-object by lexeme-string
|
34
|
+
@lexeme_table = {}
|
35
|
+
|
36
|
+
@tokens = {}
|
37
|
+
@mapped_str = []
|
38
|
+
end
|
17
39
|
|
18
|
-
|
19
|
-
|
20
|
-
|
40
|
+
def self.init(&block)
|
41
|
+
mapping = Mapping.new
|
42
|
+
mapping.instance_eval(&block)
|
43
|
+
mapping
|
44
|
+
end
|
21
45
|
|
22
|
-
|
46
|
+
# new
|
47
|
+
def token(name, pattern, converter_func)
|
48
|
+
@tokens[name] = [pattern, converter_func]
|
49
|
+
end
|
23
50
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
51
|
+
def default_tokens
|
52
|
+
@tokens.merge!(DEFAULT_TOKENS)
|
53
|
+
end
|
54
|
+
|
55
|
+
# new
|
56
|
+
def process(&block)
|
57
|
+
instance_eval(&block)
|
58
|
+
@max_value = TraceVisualization::Reorder.process(@mapped_str)
|
59
|
+
end
|
30
60
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
61
|
+
# Load data from source file. File is read line by line
|
62
|
+
def from_file(path)
|
63
|
+
raise ArgumentError, 'Argument must be a string' unless path.instance_of? String
|
64
|
+
raise ArgumentError, 'File path is not defined' unless path.empty?
|
65
|
+
raise RuntimeError, 'File doesn\'t exists' unless File.exists?(path)
|
66
|
+
|
67
|
+
fd = open(path)
|
68
|
+
process_line(line) while line = fd.gets
|
69
|
+
fd.close
|
70
|
+
end
|
71
|
+
|
72
|
+
# Load data from preprocessed file. File is read line by line
|
73
|
+
def from_preprocessed_file(path)
|
74
|
+
raise ArgumentError, 'Argument must be a string' unless path.instance_of? String
|
75
|
+
raise ArgumentError, 'File path is not defined' unless path.empty?
|
76
|
+
raise RuntimeError, 'File doesn\'t exists' unless File.exists?(path)
|
77
|
+
|
78
|
+
fd = open(path)
|
79
|
+
process_preprocessed_line line while line = fd.gets
|
80
|
+
fd.close
|
81
|
+
end
|
82
|
+
|
83
|
+
# new
|
84
|
+
def from_string(str)
|
85
|
+
raise ArgumentError, 'Argument must be a string' unless str.instance_of? String
|
86
|
+
raise ArgumentError, 'String is not defined' if str.empty?
|
87
|
+
|
88
|
+
@str = str
|
89
|
+
|
90
|
+
str.split("\n").each do |line|
|
91
|
+
process_line(line)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def from_preprocessed_string(str)
|
96
|
+
raise ArgumentError, 'Argument must be a string' if not str.instance_of? String
|
97
|
+
raise ArgumentError, 'String is not defined' if str.empty?
|
98
|
+
|
99
|
+
str.split("\n").each do |line|
|
100
|
+
process_preprocessed_line(line)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def process_preprocessed_line(line)
|
105
|
+
lexeme_positions = []
|
106
|
+
pos = 0
|
107
|
+
while (m = LEXEME_REGEXP.match(line, pos))
|
108
|
+
pos = m.begin(0)
|
109
|
+
|
110
|
+
lexeme = install_lexeme_m(m)
|
111
|
+
lexeme_positions << TraceVisualization::Data::LexemePos.new(lexeme, pos)
|
112
|
+
|
113
|
+
pos += lexeme.lexeme_length
|
114
|
+
end
|
115
|
+
|
116
|
+
pos, idx = 0, 0
|
117
|
+
while pos < line.length
|
118
|
+
lexeme = nil
|
119
|
+
if idx < lexeme_positions.size && lexeme_positions[idx].pos == pos
|
120
|
+
lexeme = lexeme_positions[idx].lexeme
|
121
|
+
idx += 1
|
46
122
|
else
|
47
|
-
|
123
|
+
lexeme = install_lexeme('CHAR', line[pos], line[pos].ord, 1)
|
48
124
|
end
|
125
|
+
pos += lexeme.lexeme_length
|
126
|
+
@mapped_str << lexeme
|
49
127
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
128
|
+
|
129
|
+
@mapped_str << install_lexeme('CHAR', "\n", "\n".ord, 1)
|
130
|
+
end
|
131
|
+
|
132
|
+
# new
|
133
|
+
def process_line(line)
|
134
|
+
lexeme_poss = []
|
135
|
+
|
136
|
+
@tokens.each do |name, value|
|
137
|
+
pattern, converter_func = value
|
138
|
+
pos = 0
|
139
|
+
while (m = pattern.match(line, pos))
|
140
|
+
lexeme_string, pos = m[:lexeme], m.begin(0)
|
141
|
+
|
142
|
+
lexeme = install_lexeme(name, lexeme_string, converter_func.call(lexeme_string), lexeme_string.length)
|
143
|
+
lexeme_poss << TraceVisualization::Data::LexemePos.new(lexeme, pos)
|
144
|
+
|
145
|
+
pos += lexeme_string.length
|
146
|
+
end
|
53
147
|
end
|
54
|
-
|
55
|
-
|
56
|
-
|
148
|
+
|
149
|
+
lexeme_poss = TraceVisualization::LexemeOverlapFilter.process(lexeme_poss)
|
150
|
+
|
151
|
+
pos, idx = 0, 0
|
152
|
+
while pos < line.length
|
153
|
+
lexeme = nil
|
154
|
+
if idx < lexeme_poss.size && lexeme_poss[idx].pos == pos
|
155
|
+
lexeme = lexeme_poss[idx].lexeme
|
156
|
+
idx += 1
|
157
|
+
else
|
158
|
+
lexeme = install_lexeme('CHAR', line[pos], line[pos].ord, 1)
|
159
|
+
end
|
160
|
+
pos += lexeme.length
|
161
|
+
@mapped_str << lexeme
|
57
162
|
end
|
58
163
|
|
59
|
-
|
60
|
-
|
164
|
+
@mapped_str << install_lexeme('CHAR', "\n", "\n".ord, 1)
|
165
|
+
end
|
166
|
+
|
167
|
+
def install_lexeme(name, lexeme_string, int_value, lexeme_length)
|
168
|
+
lexeme = @lexeme_table[lexeme_string]
|
169
|
+
|
170
|
+
if lexeme == nil
|
171
|
+
lexeme = TraceVisualization::Data::Lexeme.new(name, lexeme_string, int_value)
|
172
|
+
lexeme.lexeme_length = lexeme_length
|
173
|
+
@lexeme_table[lexeme_string] = lexeme
|
174
|
+
end
|
175
|
+
|
176
|
+
lexeme
|
177
|
+
end
|
178
|
+
|
179
|
+
def install_lexeme_m(m)
|
180
|
+
lexeme = @lexeme_table[m[:source]]
|
181
|
+
|
182
|
+
if lexeme == nil
|
183
|
+
lexeme = TraceVisualization::Data::Lexeme.new(m[:name], m[:source], m[:value].to_i)
|
184
|
+
lexeme.lexeme_length = m.to_s.length
|
185
|
+
@lexeme_table[m[:source]] = lexeme
|
61
186
|
end
|
187
|
+
|
188
|
+
lexeme
|
189
|
+
end
|
190
|
+
|
191
|
+
def [](index)
|
192
|
+
@mapped_str[index]
|
193
|
+
end
|
194
|
+
|
195
|
+
def length
|
196
|
+
@mapped_str.length
|
197
|
+
end
|
198
|
+
|
199
|
+
def size
|
200
|
+
length
|
201
|
+
end
|
202
|
+
|
203
|
+
def <<(object)
|
204
|
+
@mapped_str << Item.new(object, "unknown")
|
205
|
+
end
|
206
|
+
|
207
|
+
def pop
|
208
|
+
@mapped_str.pop
|
209
|
+
end
|
210
|
+
|
211
|
+
def max
|
212
|
+
@max_value
|
213
|
+
end
|
214
|
+
|
215
|
+
def find_all
|
216
|
+
@mapped_str.find_all { |item| yield(item) }
|
62
217
|
end
|
63
218
|
|
64
|
-
def
|
219
|
+
def restore(pos = 0, length = @mapped_str.length)
|
220
|
+
@mapped_str[pos ... pos + length].inject("") { |res, c| res += c.value }
|
221
|
+
end
|
222
|
+
|
223
|
+
def to_ary
|
224
|
+
@mapped_str.collect { |lexeme| lexeme.value }
|
225
|
+
end
|
226
|
+
|
227
|
+
def method_missing(name, *args, &blk)
|
228
|
+
raise "Missing method #{name}"
|
229
|
+
end
|
230
|
+
|
231
|
+
private
|
232
|
+
|
233
|
+
def parse(str)
|
65
234
|
map = {}
|
66
235
|
ppos = []
|
67
236
|
itemByPos = {}
|
68
237
|
|
69
|
-
|
238
|
+
DEFAULT_TOKENS.each do |type, patterns|
|
70
239
|
patterns.each do |pattern|
|
71
240
|
match(str, type, pattern, map, ppos, itemByPos)
|
72
241
|
end
|
@@ -81,20 +250,19 @@ module TraceVisualization
|
|
81
250
|
if i == ppos[j]
|
82
251
|
item = itemByPos[ppos[j]]
|
83
252
|
result << item
|
84
|
-
i
|
85
|
-
j += 1
|
253
|
+
i, j = i + item.length, j + 1
|
86
254
|
else
|
87
255
|
result << Item.new(str[i], "char")
|
88
256
|
i += 1
|
89
257
|
end
|
90
258
|
end
|
91
259
|
|
92
|
-
TraceVisualization::Reorder.process(result)
|
260
|
+
@max_value = TraceVisualization::Reorder.process(result)
|
93
261
|
|
94
262
|
result
|
95
263
|
end
|
96
264
|
|
97
|
-
def
|
265
|
+
def match(str, type, pattern, map, ppos, itemByPos)
|
98
266
|
pos = 0
|
99
267
|
|
100
268
|
limit = 1000
|
@@ -111,10 +279,5 @@ module TraceVisualization
|
|
111
279
|
end
|
112
280
|
|
113
281
|
end
|
114
|
-
|
115
|
-
def self.restore(array)
|
116
|
-
array.inject("") { |res, c| res += c.to_str }
|
117
|
-
end
|
118
|
-
|
119
282
|
end
|
120
283
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
LEX = flex
|
2
|
+
CC = gcc
|
3
|
+
CPP = g++
|
4
|
+
|
5
|
+
LDFLAGS =-lfl
|
6
|
+
|
7
|
+
CPPUNIT_BIN_CFG = cppunit-config
|
8
|
+
CPPUNIT_CFLAGS = `$(CPPUNIT_BIN_CFG) --cflags`
|
9
|
+
CPPUNIT_LIBS = `$(CPPUNIT_BIN_CFG) --libs`
|
10
|
+
|
11
|
+
CFLAGS =-O0 -Wall -g
|
12
|
+
CPPFLAGS =-O0 -Wall $(CPPUNIT_CFLAGS) -g
|
13
|
+
# CPPFLAGS =-O0 -Wall -std=c++11 $(CPPUNIT_CFLAGS)
|
14
|
+
|
15
|
+
.cpp.o :
|
16
|
+
$(CPP) $(CPPFLAGS) -o $@ -c $<
|
17
|
+
|
18
|
+
.c.o :
|
19
|
+
$(CC) $(CFLAGS) -o $@ -c $<
|
20
|
+
|
21
|
+
%.cppo : %.c
|
22
|
+
$(CPP) $(CPPFLAGS) -o $@ -c $<
|
23
|
+
|
24
|
+
preprocessor: preprocessor.o parser_functions.o
|
25
|
+
$(CC) -o $@ $(LDFLAGS) $^
|
26
|
+
|
27
|
+
preprocessor.c: preprocessor.l
|
28
|
+
$(LEX) -o $@ $^
|
29
|
+
|
30
|
+
unit_tests: test_main.o hashmap.cppo hashmap_test.o \
|
31
|
+
parser_functions.cppo parser_test.o \
|
32
|
+
lexeme_table.cppo
|
33
|
+
$(CPP) -o $@ $(CPPUNIT_LIBS) $^
|
34
|
+
|
35
|
+
unit_tests_run: unit_tests
|
36
|
+
./unit_tests
|
37
|
+
|
38
|
+
install: preprocessor
|
39
|
+
cp preprocessor ../../../bin/
|
40
|
+
make clean
|
41
|
+
|
42
|
+
clean:
|
43
|
+
rm -f *.o *.cppo preprocessor.c unit_tests preprocessor
|
@@ -0,0 +1,93 @@
|
|
1
|
+
#include "hashmap.h"
|
2
|
+
#include <stdio.h>
|
3
|
+
|
4
|
+
hashmap_t* hashmap_new() {
|
5
|
+
hashmap_t* hashmap = (hashmap_t*)malloc(sizeof(hashmap_t));
|
6
|
+
|
7
|
+
hashmap->size = 0;
|
8
|
+
hashmap->table = (hashmap_element**)calloc(HASHMAP_SIZE, sizeof(hashmap_element*));
|
9
|
+
|
10
|
+
return hashmap;
|
11
|
+
}
|
12
|
+
|
13
|
+
void* hashmap_get(hashmap_t* map, long key) {
|
14
|
+
hashmap_element* ptr;
|
15
|
+
int idx = (int)(key % HASHMAP_SIZE);
|
16
|
+
|
17
|
+
for (ptr = map->table[idx]; ptr != NULL; ptr = ptr->next) {
|
18
|
+
if (ptr->key == key) {
|
19
|
+
return ptr->value;
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
return NULL;
|
24
|
+
}
|
25
|
+
|
26
|
+
int hashmap_put(hashmap_t* map, long key, void* value) {
|
27
|
+
int result = 1;
|
28
|
+
|
29
|
+
if (hashmap_get(map, key) == NULL) {
|
30
|
+
hashmap_element* ptr;
|
31
|
+
hashmap_element* element = (hashmap_element*) malloc(sizeof(hashmap_element));
|
32
|
+
|
33
|
+
element->key = key;
|
34
|
+
element->value = value;
|
35
|
+
element->next = NULL;
|
36
|
+
|
37
|
+
int idx = (int)(key % HASHMAP_SIZE);
|
38
|
+
ptr = map->table[idx];
|
39
|
+
|
40
|
+
if (ptr) {
|
41
|
+
while (ptr->next) ptr = ptr->next;
|
42
|
+
ptr->next = element;
|
43
|
+
} else {
|
44
|
+
map->table[idx] = element;
|
45
|
+
}
|
46
|
+
|
47
|
+
map->size += 1;
|
48
|
+
|
49
|
+
result = 0;
|
50
|
+
}
|
51
|
+
|
52
|
+
return result;
|
53
|
+
}
|
54
|
+
|
55
|
+
void** hashmap_values(hashmap_t* map) {
|
56
|
+
int i = 0;
|
57
|
+
int j = 0;
|
58
|
+
hashmap_element* ptr;
|
59
|
+
void** values = (void**)malloc(map->size * sizeof(void*));
|
60
|
+
|
61
|
+
for (; i < HASHMAP_SIZE; i += 1) {
|
62
|
+
ptr = map->table[i];
|
63
|
+
|
64
|
+
while (ptr) {
|
65
|
+
values[j] = ptr->value;
|
66
|
+
ptr = ptr->next;
|
67
|
+
j += 1;
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
return values;
|
72
|
+
}
|
73
|
+
|
74
|
+
void hashmap_values_free(void** values) {
|
75
|
+
free(values);
|
76
|
+
}
|
77
|
+
|
78
|
+
void hashmap_free(hashmap_t* map) {
|
79
|
+
hashmap_element* ptr;
|
80
|
+
hashmap_element* tmp;
|
81
|
+
int i;
|
82
|
+
for (i = 0; i < HASHMAP_SIZE; i++) {
|
83
|
+
for (ptr = map->table[i]; ptr != NULL; ) {
|
84
|
+
tmp = ptr;
|
85
|
+
ptr = (hashmap_element*)ptr->next;
|
86
|
+
|
87
|
+
free(tmp);
|
88
|
+
}
|
89
|
+
}
|
90
|
+
free(map);
|
91
|
+
}
|
92
|
+
|
93
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#ifndef __HASHMAP_H__
|
2
|
+
#define __HASHMAP_H__
|
3
|
+
|
4
|
+
#include <stdlib.h>
|
5
|
+
|
6
|
+
#define HASHMAP_SIZE (256)
|
7
|
+
|
8
|
+
// typedef struct hashmap_element hashmap_element;
|
9
|
+
struct hashmap_element {
|
10
|
+
long key;
|
11
|
+
void* value;
|
12
|
+
hashmap_element * next;
|
13
|
+
};
|
14
|
+
|
15
|
+
struct hashmap_t {
|
16
|
+
hashmap_element** table;
|
17
|
+
int size;
|
18
|
+
};
|
19
|
+
|
20
|
+
extern hashmap_t* hashmap_new();
|
21
|
+
extern int hashmap_put(hashmap_t* map, long key, void* value);
|
22
|
+
extern void* hashmap_get(hashmap_t* map, long key);
|
23
|
+
extern void** hashmap_values(hashmap_t* map);
|
24
|
+
extern void hashmap_values_free(void** values);
|
25
|
+
extern void hashmap_free(hashmap_t* map);
|
26
|
+
|
27
|
+
#endif
|