unistr/u8-strchr: Fix several bugs.
[gnulib.git] / lib / unistr / u8-strchr.c
1 /* Search character in UTF-8 string.
2    Copyright (C) 1999, 2002, 2006-2007, 2009-2010 Free Software Foundation,
3    Inc.
4    Written by Bruno Haible <bruno@clisp.org>, 2002.
5
6    This program is free software: you can redistribute it and/or modify it
7    under the terms of the GNU Lesser General Public License as published
8    by the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19 #include <config.h>
20
21 /* Specification.  */
22 #include "unistr.h"
23
24 #include <string.h>
25
26 uint8_t *
27 u8_strchr (const uint8_t *s, ucs4_t uc)
28 {
29   uint8_t c[6];
30
31   if (uc < 0x80)
32     {
33       uint8_t c0 = uc;
34
35       if (false)
36         {
37           /* Unoptimized code.  */
38           for (;;)
39             {
40               uint8_t s0 = *s;
41               if (s0 == c0)
42                 return (uint8_t *) s;
43               s++;
44               if (s0 == 0)
45                 break;
46             }
47         }
48       else
49         {
50           /* Optimized code.
51              strchr() is often so well optimized, that it's worth the
52              added function call.  */
53           return (uint8_t *) strchr ((const char *) s, c0);
54         }
55     }
56   else
57       /* Loops equivalent to strstr, optimized for a specific length (2, 3, 4)
58          of the needle.  We use an algorithm similar to Boyer-Moore which
59          is documented in lib/unistr/u8-chr.c.  There is additional
60          complication because we need to check after every byte for
61          a NUL byte, but the idea is the same. */
62     switch (u8_uctomb_aux (c, uc, 6))
63       {
64       case 2:
65         if (*s == 0 || s[1] == 0)
66           break;
67         {
68           uint8_t c0 = c[0];
69           uint8_t c1 = c[1];
70           uint8_t s1 = s[1];
71
72           for (;;)
73             {
74               if (s1 == c1)
75                 {
76                   if (*s == c0)
77                     return (uint8_t *) s;
78                   else
79                     goto case2_skip2;
80                 }
81               else
82                 {
83                   if (s1 == c0)
84                     goto case2_skip1;
85                   else
86                     goto case2_skip2;
87                 }
88              case2_skip2:
89               s++;
90               s1 = s[1];
91               if (s[1] == 0)
92                 break;
93              case2_skip1:
94               s++;
95               s1 = s[1];
96               if (s[1] == 0)
97                 break;
98             }
99         }
100         break;
101
102       case 3:
103         if (*s == 0 || s[1] == 0 || s[2] == 0)
104           break;
105         {
106           uint8_t c0 = c[0];
107           uint8_t c1 = c[1];
108           uint8_t c2 = c[2];
109           uint8_t s2 = s[2];
110
111           for (;;)
112             {
113               if (s2 == c2)
114                 {
115                   if (s[1] == c1 && *s == c0)
116                     return (uint8_t *) s;
117                   else if (c2 == c1)
118                     goto case3_skip1;
119                   else
120                     goto case3_skip3;
121                 }
122               else
123                 {
124                   if (s2 == c1)
125                     goto case3_skip1;
126                   else if (s2 == c0)
127                     goto case3_skip2;
128                   else
129                     goto case3_skip3;
130                 }
131              case3_skip3:
132               s++;
133               s2 = s[2];
134               if (s[2] == 0)
135                 break;
136              case3_skip2:
137               s++;
138               s2 = s[2];
139               if (s[2] == 0)
140                 break;
141              case3_skip1:
142               s++;
143               s2 = s[2];
144               if (s[2] == 0)
145                 break;
146             }
147         }
148
149       case 4:
150         if (*s == 0 || s[1] == 0 || s[2] == 0 || s[3] == 0)
151           break;
152         {
153           uint8_t c0 = c[0];
154           uint8_t c1 = c[1];
155           uint8_t c2 = c[2];
156           uint8_t c3 = c[3];
157           uint8_t s3 = s[3];
158
159           for (;;)
160             {
161               if (s3 == c3)
162                 {
163                   if (s[2] == c2 && s[1] == c1 && *s == c0)
164                     return (uint8_t *) s;
165                   else if (c3 == c2)
166                     goto case4_skip1;
167                   else if (c3 == c1)
168                     goto case4_skip2;
169                   else
170                     goto case4_skip4;
171                 }
172               else
173                 {
174                   if (s3 == c2)
175                     goto case4_skip1;
176                   else if (s3 == c1)
177                     goto case4_skip2;
178                   else if (s3 == c0)
179                     goto case4_skip3;
180                   else
181                     goto case4_skip4;
182                 }
183              case4_skip4:
184               s++;
185               s3 = s[3];
186               if (s[3] == 0)
187                 break;
188              case4_skip3:
189               s++;
190               s3 = s[3];
191               if (s[3] == 0)
192                 break;
193              case4_skip2:
194               s++;
195               s3 = s[3];
196               if (s[3] == 0)
197                 break;
198              case4_skip1:
199               s++;
200               s3 = s[3];
201               if (s[3] == 0)
202                 break;
203             }
204         }
205       }
206
207   return NULL;
208 }