1 /** @file
2 
3   A brief file description
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 /****************************************************************************
25 
26   ink_string.h
27 
28   String and text processing routines for libts
29 
30  ****************************************************************************/
31 
32 #pragma once
33 
34 #include <cstdio>
35 #include <memory.h>
36 #include <strings.h>
37 #include <string_view>
38 
39 #include "tscore/ink_assert.h"
40 #include "tscore/ink_error.h"
41 #include "tscore/ParseRules.h"
42 #include "tscore/ink_apidefs.h"
43 
44 /*===========================================================================*
45 
46                             Function Prototypes
47 
48  *===========================================================================*/
49 /* these are supposed to be fast */
50 
51 inkcoreapi char *ink_memcpy_until_char(char *dst, char *src, unsigned int n, unsigned char c);
52 inkcoreapi char *ink_string_concatenate_strings(char *dest, ...);
53 inkcoreapi char *ink_string_concatenate_strings_n(char *dest, int n, ...);
54 inkcoreapi char *ink_string_append(char *dest, char *src, int n);
55 
56 /*
57  * Copy src to string dst of size siz.  At most siz-1 characters
58  * will be copied.  Always NUL terminates (unless siz == 0).
59  * Returns strlen(src); if retval >= siz, truncation occurred.
60  */
61 #if HAVE_STRLCPY
62 #define ink_strlcpy strlcpy
63 #else
64 size_t ink_strlcpy(char *dst, const char *str, size_t siz);
65 #endif
66 /*
67  * Appends src to string dst of size siz (unlike strncat, siz is the
68  * full size of dst, not space left).  At most siz-1 characters
69  * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
70  * Returns strlen(src) + MIN(siz, strlen(initial dst)).
71  * If retval >= siz, truncation occurred.
72  */
73 #if HAVE_STRLCAT
74 #define ink_strlcat strlcat
75 #else
76 size_t ink_strlcat(char *dst, const char *str, size_t siz);
77 #endif
78 
79 /* Convert from UTF-8 to latin-1/iso-8859-1.  This can be lossy. */
80 void ink_utf8_to_latin1(const char *in, int inlen, char *out, int *outlen);
81 
82 /*===========================================================================*
83 
84                              Inline Functions
85 
86  *===========================================================================*/
87 
88 // inline int ptr_len_casecmp(const char* p1, int l1, const char* p2, int l2)
89 //
90 //     strcasecmp() functionality for two ptr length pairs
91 //
92 inline int
ptr_len_casecmp(const char * p1,int l1,const char * p2,int l2)93 ptr_len_casecmp(const char *p1, int l1, const char *p2, int l2)
94 {
95   if (l1 < l2) {
96     return -1;
97   } else if (l1 > l2) {
98     return 1;
99   }
100 
101   while (l1) {
102     char p1c = ParseRules::ink_tolower(*p1);
103     char p2c = ParseRules::ink_tolower(*p2);
104 
105     if (p1c != p2c) {
106       if (p1c > p2c) {
107         return 1;
108       } else {
109         return -1;
110       }
111     }
112 
113     p1++;
114     p2++;
115     l1--;
116   }
117 
118   return 0;
119 }
120 
121 // inline const char* ptr_len_str(const char* p1, int l1, const char* str)
122 //
123 //   strstr() like functionality for the ptr, len pairs
124 //
125 inline const char *
ptr_len_str(const char * p1,int l1,const char * str)126 ptr_len_str(const char *p1, int l1, const char *str)
127 {
128   if (str && str[0]) {
129     int str_index           = 0;
130     const char *match_start = nullptr;
131 
132     while (l1 > 0) {
133       if (*p1 == str[str_index]) {
134         // If this is the start of a match,
135         //    record it;
136         if (str_index == 0) {
137           match_start = p1;
138         }
139         // Check to see if we are finished;
140         str_index++;
141         if (str[str_index] == '\0') {
142           return match_start;
143         }
144       } else if (str_index > 0) {
145         l1 += (p1 - match_start);
146         p1        = match_start;
147         str_index = 0;
148       }
149 
150       p1++;
151       l1--;
152     }
153   }
154   return nullptr;
155 }
156 
157 // int ptr_len_ncmp(const char* p1, int l1, const char* str, int n) {
158 //
159 //    strncmp like functionality for comparing a ptr,len pair with
160 //       a null terminated string for n chars
161 //
162 inline int
ptr_len_ncmp(const char * p1,int l1,const char * str,int n)163 ptr_len_ncmp(const char *p1, int l1, const char *str, int n)
164 {
165   while (l1 > 0 && n > 0) {
166     if (*str == '\0') {
167       return 1;
168     }
169 
170     char p1c  = *p1;
171     char strc = *str;
172 
173     if (p1c != strc) {
174       if (p1c > strc) {
175         return 1;
176       } else if (p1c < strc) {
177         return -1;
178       }
179     }
180 
181     p1++;
182     l1--;
183     n--;
184     str++;
185   }
186 
187   // If we've scanned our nchars, the match
188   //   otherwise we're here because str is longer
189   //   than p1
190 
191   if (n == 0) {
192     return 0;
193   } else {
194     return -1;
195   }
196 }
197 
198 // int ptr_len_ncasecmp(const char* p1, int l1, const char* str, int n) {
199 //
200 //    strncasecmp like functionality for comparing a ptr,len pair with
201 //       a null terminated string for n chars
202 //
203 inline int
ptr_len_ncasecmp(const char * p1,int l1,const char * str,int n)204 ptr_len_ncasecmp(const char *p1, int l1, const char *str, int n)
205 {
206   while (l1 > 0 && n > 0) {
207     if (*str == '\0') {
208       return 1;
209     }
210 
211     char p1c  = ParseRules::ink_tolower(*p1);
212     char strc = ParseRules::ink_tolower(*str);
213 
214     if (p1c != strc) {
215       if (p1c > strc) {
216         return 1;
217       } else if (p1c < strc) {
218         return -1;
219       }
220     }
221 
222     p1++;
223     l1--;
224     n--;
225     str++;
226   }
227 
228   // If we've scanned our nchars, the match
229   //   otherwise we're here because str is longer
230   //   than p1
231 
232   if (n == 0) {
233     return 0;
234   } else {
235     return -1;
236   }
237 }
238 
239 // int ptr_len_casecmp(const char* p1, int l1, const char* str) {
240 //
241 //    strcasecmp like functionality for comparing a ptr,len pair with
242 //       a null terminated string
243 //
244 inline int
ptr_len_casecmp(const char * p1,int l1,const char * str)245 ptr_len_casecmp(const char *p1, int l1, const char *str)
246 {
247   while (l1 > 0) {
248     if (*str == '\0') {
249       return 1;
250     }
251 
252     char p1c  = ParseRules::ink_tolower(*p1);
253     char strc = ParseRules::ink_tolower(*str);
254 
255     if (p1c != strc) {
256       if (p1c > strc) {
257         return 1;
258       } else if (p1c < strc) {
259         return -1;
260       }
261     }
262 
263     p1++;
264     l1--;
265     str++;
266   }
267 
268   // Since we're out of characters in p1
269   //   str needs to be finished for the strings
270   //   to get equal
271   if (*str == '\0') {
272     return 0;
273   } else {
274     return -1;
275   }
276 }
277 
278 // char* ptr_len_pbrk(const char* p1, int l1, const char* str)
279 //
280 //   strpbrk() like functionality for ptr & len pair strings
281 //
282 inline char *
ptr_len_pbrk(const char * p1,int l1,const char * str)283 ptr_len_pbrk(const char *p1, int l1, const char *str)
284 {
285   while (l1 > 0) {
286     const char *str_cur = str;
287 
288     while (*str_cur != '\0') {
289       if (*p1 == *str_cur) {
290         return (char *)p1;
291       }
292       str_cur++;
293     }
294 
295     p1++;
296     l1--;
297   }
298 
299   return nullptr;
300 }
301 
302 // Specialized "itoa", that is optimized for small integers, and use snprintf() otherwise.
303 // On error, we'll return 0, and nothing is written to the buffer.
304 // TODO: Do these really need to be inline?
305 inline int
ink_small_itoa(int val,char * buf,int buf_len)306 ink_small_itoa(int val, char *buf, int buf_len)
307 {
308   ink_assert(buf_len > 5);
309   ink_assert((val >= 0) && (val < 100000));
310 
311   if (val < 10) { // 0 - 9
312     buf[0] = '0' + val;
313     return 1;
314   } else if (val < 100) { // 10 - 99
315     buf[1] = '0' + (val % 10);
316     val /= 10;
317     buf[0] = '0' + (val % 10);
318     return 2;
319   } else if (val < 1000) { // 100 - 999
320     buf[2] = '0' + (val % 10);
321     val /= 10;
322     buf[1] = '0' + (val % 10);
323     val /= 10;
324     buf[0] = '0' + (val % 10);
325     return 3;
326   } else if (val < 10000) { // 1000 - 9999
327     buf[3] = '0' + (val % 10);
328     val /= 10;
329     buf[2] = '0' + (val % 10);
330     val /= 10;
331     buf[1] = '0' + (val % 10);
332     val /= 10;
333     buf[0] = '0' + (val % 10);
334     return 4;
335   } else { // 10000 - 99999
336     buf[4] = '0' + (val % 10);
337     val /= 10;
338     buf[3] = '0' + (val % 10);
339     val /= 10;
340     buf[2] = '0' + (val % 10);
341     val /= 10;
342     buf[1] = '0' + (val % 10);
343     val /= 10;
344     buf[0] = '0' + (val % 10);
345     return 5;
346   }
347 }
348 
349 inline int
ink_fast_itoa(int32_t val,char * buf,int buf_len)350 ink_fast_itoa(int32_t val, char *buf, int buf_len)
351 {
352   if ((val < 0) || (val > 99999)) {
353     int ret = snprintf(buf, buf_len, "%d", val);
354 
355     return (ret >= 0 ? ret : 0);
356   }
357 
358   return ink_small_itoa((int)val, buf, buf_len);
359 }
360 
361 inline int
ink_fast_uitoa(uint32_t val,char * buf,int buf_len)362 ink_fast_uitoa(uint32_t val, char *buf, int buf_len)
363 {
364   if (val > 99999) {
365     int ret = snprintf(buf, buf_len, "%u", val);
366 
367     return (ret >= 0 ? ret : 0);
368   }
369 
370   return ink_small_itoa((int)val, buf, buf_len);
371 }
372 
373 inline int
ink_fast_ltoa(int64_t val,char * buf,int buf_len)374 ink_fast_ltoa(int64_t val, char *buf, int buf_len)
375 {
376   if ((val < 0) || (val > 99999)) {
377     int ret = snprintf(buf, buf_len, "%" PRId64 "", val);
378 
379     return (ret >= 0 ? ret : 0);
380   }
381 
382   return ink_small_itoa((int)val, buf, buf_len);
383 }
384 
385 /// Check for prefix.
386 /// @return @c true if @a lhs is a prefix (ignoring case) of @a rhs.
387 inline bool
IsNoCasePrefixOf(std::string_view const & lhs,std::string_view const & rhs)388 IsNoCasePrefixOf(std::string_view const &lhs, std::string_view const &rhs)
389 {
390   return lhs.size() <= rhs.size() && 0 == strncasecmp(lhs.data(), rhs.data(), lhs.size());
391 }
392 
393 /// Check for prefix.
394 /// @return @c true if @a lhs is a prefix of @a rhs.
395 inline bool
IsPrefixOf(std::string_view const & lhs,std::string_view const & rhs)396 IsPrefixOf(std::string_view const &lhs, std::string_view const &rhs)
397 {
398   return lhs.size() <= rhs.size() && 0 == memcmp(lhs.data(), rhs.data(), lhs.size());
399 }
400