1 /* GLIB - Library of useful routines for C programming
2 *
3 * Copyright (C) 2010 Mikhail Zabaluev <mikhail.zabaluev@gmail.com>
4 *
5 * SPDX-License-Identifier: LGPL-2.1-or-later
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <string.h>
22
23 #include <glib.h>
24
25 static guint num_iterations = 0;
26
27 static const char str_ascii[] =
28 "The quick brown fox jumps over the lazy dog";
29
30 static const gchar str_latin1[] =
31 "Zwölf Boxkämpfer jagen Viktor quer über den großen Sylter Deich";
32
33 /* Energizing GOELRO-talk in Russian, used by KDE */
34 static const char str_cyrillic[] =
35 "Широкая электрификация южных губерний даст мощный толчок подъёму "
36 "сельского хозяйства.";
37
38 /* First sentence from the Wikipedia article:
39 * http://zh.wikipedia.org/w/index.php?title=%E6%B1%89%E5%AD%97&oldid=13053137 */
40 static const char str_han[] =
41 "漢字,亦稱中文字、中国字,在台灣又被稱為國字,是漢字文化圈廣泛使用的一種文字,屬於表意文字的詞素音節文字";
42
43 typedef int (* GrindFunc) (const char *, gsize);
44
45 #define GRIND_LOOP_BEGIN \
46 { \
47 guint i; \
48 for (i = 0; i < num_iterations; i++)
49
50 #define GRIND_LOOP_END \
51 }
52
53 static int
54 grind_get_char (const char *str, gsize len)
55 {
56 gunichar acc = 0;
57 GRIND_LOOP_BEGIN
58 {
59 const char *p = str;
60 while (*p)
61 {
62 acc += g_utf8_get_char (p);
63 p = g_utf8_next_char (p);
64 }
65 }
66 GRIND_LOOP_END;
67 return acc;
68 }
69
70 static int
71 grind_get_char_validated (const char *str, gsize len)
72 {
73 gunichar acc = 0;
74 GRIND_LOOP_BEGIN
75 {
76 const char *p = str;
77 while (*p)
78 {
79 acc += g_utf8_get_char_validated (p, -1);
80 p = g_utf8_next_char (p);
81 }
82 }
83 GRIND_LOOP_END;
84 return acc;
85 }
86
87 static int
88 grind_utf8_to_ucs4 (const char *str, gsize len)
89 {
90 GRIND_LOOP_BEGIN
91 {
92 gunichar *ustr;
93 ustr = g_utf8_to_ucs4 (str, -1, NULL, NULL, NULL);
94 g_free (ustr);
95 }
96 GRIND_LOOP_END;
97 return 0;
98 }
99
100 static int
101 grind_get_char_backwards (const char *str, gsize len)
102 {
103 gunichar acc = 0;
104 GRIND_LOOP_BEGIN
105 {
106 const char *p = str + len;
107 do
108 {
109 p = g_utf8_prev_char (p);
110 acc += g_utf8_get_char (p);
111 }
112 while (p != str);
113 }
114 GRIND_LOOP_END;
115 return acc;
116 }
117
118 static int
119 grind_utf8_to_ucs4_sized (const char *str, gsize len)
120 {
121 GRIND_LOOP_BEGIN
122 {
123 gunichar *ustr;
124 ustr = g_utf8_to_ucs4 (str, len, NULL, NULL, NULL);
125 g_free (ustr);
126 }
127 GRIND_LOOP_END;
128 return 0;
129 }
130
131 static int
132 grind_utf8_to_ucs4_fast (const char *str, gsize len)
133 {
134 GRIND_LOOP_BEGIN
135 {
136 gunichar *ustr;
137 ustr = g_utf8_to_ucs4_fast (str, -1, NULL);
138 g_free (ustr);
139 }
140 GRIND_LOOP_END;
141 return 0;
142 }
143
144 static int
145 grind_utf8_to_ucs4_fast_sized (const char *str, gsize len)
146 {
147 GRIND_LOOP_BEGIN
148 {
149 gunichar *ustr;
150 ustr = g_utf8_to_ucs4_fast (str, len, NULL);
151 g_free (ustr);
152 }
153 GRIND_LOOP_END;
154 return 0;
155 }
156
157 static int
158 grind_utf8_validate (const char *str, gsize len)
159 {
160 GRIND_LOOP_BEGIN
161 g_utf8_validate (str, -1, NULL);
162 GRIND_LOOP_END;
163 return 0;
164 }
165
166 static int
167 grind_utf8_validate_sized (const char *str, gsize len)
168 {
169 GRIND_LOOP_BEGIN
170 g_utf8_validate (str, len, NULL);
171 GRIND_LOOP_END;
172 return 0;
173 }
174
175 typedef struct _GrindData {
176 GrindFunc func;
177 const char *str;
178 } GrindData;
179
180 static void
181 perform (gconstpointer data)
182 {
183 GrindData *gd = (GrindData *) data;
184 GrindFunc grind_func = gd->func;
185 const char *str = gd->str;
186 gsize len;
187 gulong bytes_ground;
188 gdouble time_elapsed;
189 gdouble result;
190
191 len = strlen (str);
192 bytes_ground = (gulong) len * num_iterations;
193
194 g_test_timer_start ();
195
196 grind_func (str, len);
197
198 time_elapsed = g_test_timer_elapsed ();
199
200 result = ((gdouble) bytes_ground / time_elapsed) * 1.0e-6;
201
202 g_test_maximized_result (result, "%7.1f MB/s", result);
203
204 g_slice_free (GrindData, gd);
205 }
206
207 static void
208 add_cases(const char *path, GrindFunc func)
209 {
210 #define ADD_CASE(script) \
211 G_STMT_START { \
212 GrindData *gd; \
213 gchar *full_path; \
214 gd = g_slice_new0(GrindData); \
215 gd->func = func; \
216 gd->str = str_##script; \
217 full_path = g_strdup_printf("%s/" #script, path); \
218 g_test_add_data_func (full_path, gd, perform); \
219 g_free (full_path); \
220 } G_STMT_END
221
222 ADD_CASE(ascii);
223 ADD_CASE(latin1);
224 ADD_CASE(cyrillic);
225 ADD_CASE(han);
226
227 #undef ADD_CASE
228 }
229
230 int
231 main (int argc, char **argv)
232 {
233 g_test_init (&argc, &argv, NULL);
234
235 num_iterations = g_test_perf () ? 500000 : 1;
236
237 add_cases ("/utf8/perf/get_char", grind_get_char);
238 add_cases ("/utf8/perf/get_char-backwards", grind_get_char_backwards);
239 add_cases ("/utf8/perf/get_char_validated", grind_get_char_validated);
240 add_cases ("/utf8/perf/utf8_to_ucs4", grind_utf8_to_ucs4);
241 add_cases ("/utf8/perf/utf8_to_ucs4-sized", grind_utf8_to_ucs4_sized);
242 add_cases ("/utf8/perf/utf8_to_ucs4_fast", grind_utf8_to_ucs4_fast);
243 add_cases ("/utf8/perf/utf8_to_ucs4_fast-sized", grind_utf8_to_ucs4_fast_sized);
244 add_cases ("/utf8/perf/utf8_validate", grind_utf8_validate);
245 add_cases ("/utf8/perf/utf8_validate-sized", grind_utf8_validate_sized);
246
247 return g_test_run ();
248 }