1/*
2 * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License").
5 * You may not use this file except in compliance with the License.
6 * A copy of the License is located at
7 *
8 * http://aws.amazon.com/apache2.0
9 *
10 * or in the "license" file accompanying this file. This file is distributed
11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 * express or implied. See the License for the specific language governing
13 * permissions and limitations under the License.
14 */
15#include <aws/common/string.h>
16
17struct aws_string *aws_string_new_from_c_str(struct aws_allocator *allocator, const char *c_str) {
18 AWS_PRECONDITION(allocator && c_str);
19 return aws_string_new_from_array(allocator, (const uint8_t *)c_str, strlen(c_str));
20}
21
22struct aws_string *aws_string_new_from_array(struct aws_allocator *allocator, const uint8_t *bytes, size_t len) {
23 AWS_PRECONDITION(allocator);
24 AWS_PRECONDITION(AWS_MEM_IS_READABLE(bytes, len));
25 size_t malloc_size;
26 if (aws_add_size_checked(sizeof(struct aws_string) + 1, len, &malloc_size)) {
27 return NULL;
28 }
29 struct aws_string *str = aws_mem_acquire(allocator, malloc_size);
30 if (!str) {
31 return NULL;
32 }
33
34 /* Fields are declared const, so we need to copy them in like this */
35 *(struct aws_allocator **)(&str->allocator) = allocator;
36 *(size_t *)(&str->len) = len;
37 memcpy((void *)str->bytes, bytes, len);
38 *(uint8_t *)&str->bytes[len] = '\0';
39 AWS_RETURN_WITH_POSTCONDITION(str, aws_string_is_valid(str));
40}
41
42struct aws_string *aws_string_new_from_string(struct aws_allocator *allocator, const struct aws_string *str) {
43 AWS_PRECONDITION(allocator && aws_string_is_valid(str));
44 return aws_string_new_from_array(allocator, str->bytes, str->len);
45}
46
47void aws_string_destroy(struct aws_string *str) {
48 AWS_PRECONDITION(!str || aws_string_is_valid(str));
49 if (str && str->allocator) {
50 aws_mem_release(str->allocator, str);
51 }
52}
53
54void aws_string_destroy_secure(struct aws_string *str) {
55 AWS_PRECONDITION(!str || aws_string_is_valid(str));
56 if (str) {
57 aws_secure_zero((void *)aws_string_bytes(str), str->len);
58 if (str->allocator) {
59 aws_mem_release(str->allocator, str);
60 }
61 }
62}
63
64int aws_string_compare(const struct aws_string *a, const struct aws_string *b) {
65 AWS_PRECONDITION(!a || aws_string_is_valid(a));
66 AWS_PRECONDITION(!b || aws_string_is_valid(b));
67 if (a == b) {
68 return 0; /* strings identical */
69 }
70 if (a == NULL) {
71 return -1;
72 }
73 if (b == NULL) {
74 return 1;
75 }
76
77 size_t len_a = a->len;
78 size_t len_b = b->len;
79 size_t min_len = len_a < len_b ? len_a : len_b;
80
81 int ret = memcmp(aws_string_bytes(a), aws_string_bytes(b), min_len);
82 AWS_POSTCONDITION(aws_string_is_valid(a));
83 AWS_POSTCONDITION(aws_string_is_valid(b));
84 if (ret) {
85 return ret; /* overlapping characters differ */
86 }
87 if (len_a == len_b) {
88 return 0; /* strings identical */
89 }
90 if (len_a > len_b) {
91 return 1; /* string b is first n characters of string a */
92 }
93 return -1; /* string a is first n characters of string b */
94}
95
96int aws_array_list_comparator_string(const void *a, const void *b) {
97 if (a == b) {
98 return 0; /* strings identical */
99 }
100 if (a == NULL) {
101 return -1;
102 }
103 if (b == NULL) {
104 return 1;
105 }
106 const struct aws_string *str_a = *(const struct aws_string **)a;
107 const struct aws_string *str_b = *(const struct aws_string **)b;
108 return aws_string_compare(str_a, str_b);
109}
110
111/**
112 * Returns true if bytes of string are the same, false otherwise.
113 */
114bool aws_string_eq(const struct aws_string *a, const struct aws_string *b) {
115 AWS_PRECONDITION(!a || aws_string_is_valid(a));
116 AWS_PRECONDITION(!b || aws_string_is_valid(b));
117 if (a == b) {
118 return true;
119 }
120 if (a == NULL || b == NULL) {
121 return false;
122 }
123 return aws_array_eq(a->bytes, a->len, b->bytes, b->len);
124}
125
126/**
127 * Returns true if bytes of string are equivalent, using a case-insensitive comparison.
128 */
129bool aws_string_eq_ignore_case(const struct aws_string *a, const struct aws_string *b) {
130 AWS_PRECONDITION(!a || aws_string_is_valid(a));
131 AWS_PRECONDITION(!b || aws_string_is_valid(b));
132 if (a == b) {
133 return true;
134 }
135 if (a == NULL || b == NULL) {
136 return false;
137 }
138 return aws_array_eq_ignore_case(a->bytes, a->len, b->bytes, b->len);
139}
140
141/**
142 * Returns true if bytes of string and cursor are the same, false otherwise.
143 */
144bool aws_string_eq_byte_cursor(const struct aws_string *str, const struct aws_byte_cursor *cur) {
145 AWS_PRECONDITION(!str || aws_string_is_valid(str));
146 AWS_PRECONDITION(!cur || aws_byte_cursor_is_valid(cur));
147 if (str == NULL && cur == NULL) {
148 return true;
149 }
150 if (str == NULL || cur == NULL) {
151 return false;
152 }
153 return aws_array_eq(str->bytes, str->len, cur->ptr, cur->len);
154}
155
156/**
157 * Returns true if bytes of string and cursor are equivalent, using a case-insensitive comparison.
158 */
159
160bool aws_string_eq_byte_cursor_ignore_case(const struct aws_string *str, const struct aws_byte_cursor *cur) {
161 AWS_PRECONDITION(!str || aws_string_is_valid(str));
162 AWS_PRECONDITION(!cur || aws_byte_cursor_is_valid(cur));
163 if (str == NULL && cur == NULL) {
164 return true;
165 }
166 if (str == NULL || cur == NULL) {
167 return false;
168 }
169 return aws_array_eq_ignore_case(str->bytes, str->len, cur->ptr, cur->len);
170}
171
172/**
173 * Returns true if bytes of string and buffer are the same, false otherwise.
174 */
175bool aws_string_eq_byte_buf(const struct aws_string *str, const struct aws_byte_buf *buf) {
176 AWS_PRECONDITION(!str || aws_string_is_valid(str));
177 AWS_PRECONDITION(!buf || aws_byte_buf_is_valid(buf));
178 if (str == NULL && buf == NULL) {
179 return true;
180 }
181 if (str == NULL || buf == NULL) {
182 return false;
183 }
184 return aws_array_eq(str->bytes, str->len, buf->buffer, buf->len);
185}
186
187/**
188 * Returns true if bytes of string and buffer are equivalent, using a case-insensitive comparison.
189 */
190
191bool aws_string_eq_byte_buf_ignore_case(const struct aws_string *str, const struct aws_byte_buf *buf) {
192 AWS_PRECONDITION(!str || aws_string_is_valid(str));
193 AWS_PRECONDITION(!buf || aws_byte_buf_is_valid(buf));
194 if (str == NULL && buf == NULL) {
195 return true;
196 }
197 if (str == NULL || buf == NULL) {
198 return false;
199 }
200 return aws_array_eq_ignore_case(str->bytes, str->len, buf->buffer, buf->len);
201}
202
203bool aws_string_eq_c_str(const struct aws_string *str, const char *c_str) {
204 AWS_PRECONDITION(!str || aws_string_is_valid(str));
205 if (str == NULL && c_str == NULL) {
206 return true;
207 }
208 if (str == NULL || c_str == NULL) {
209 return false;
210 }
211 return aws_array_eq_c_str(str->bytes, str->len, c_str);
212}
213
214/**
215 * Returns true if bytes of strings are equivalent, using a case-insensitive comparison.
216 */
217bool aws_string_eq_c_str_ignore_case(const struct aws_string *str, const char *c_str) {
218 AWS_PRECONDITION(!str || aws_string_is_valid(str));
219 if (str == NULL && c_str == NULL) {
220 return true;
221 }
222 if (str == NULL || c_str == NULL) {
223 return false;
224 }
225 return aws_array_eq_c_str_ignore_case(str->bytes, str->len, c_str);
226}
227
228bool aws_byte_buf_write_from_whole_string(
229 struct aws_byte_buf *AWS_RESTRICT buf,
230 const struct aws_string *AWS_RESTRICT src) {
231 AWS_PRECONDITION(!buf || aws_byte_buf_is_valid(buf));
232 AWS_PRECONDITION(!src || aws_string_is_valid(src));
233 if (buf == NULL || src == NULL) {
234 return false;
235 }
236 return aws_byte_buf_write(buf, aws_string_bytes(src), src->len);
237}
238
239/**
240 * Creates an aws_byte_cursor from an existing string.
241 */
242struct aws_byte_cursor aws_byte_cursor_from_string(const struct aws_string *src) {
243 AWS_PRECONDITION(aws_string_is_valid(src));
244 return aws_byte_cursor_from_array(aws_string_bytes(src), src->len);
245}
246
247struct aws_string *aws_string_clone_or_reuse(struct aws_allocator *allocator, const struct aws_string *str) {
248 AWS_PRECONDITION(allocator);
249 AWS_PRECONDITION(aws_string_is_valid(str));
250
251 if (str->allocator == NULL) {
252 /* Since the string cannot be deallocated, we assume that it will remain valid for the lifetime of the
253 * application */
254 AWS_POSTCONDITION(aws_string_is_valid(str));
255 return (struct aws_string *)str;
256 }
257
258 AWS_POSTCONDITION(aws_string_is_valid(str));
259 return aws_string_new_from_string(allocator, str);
260}
261
262int aws_secure_strlen(const char *str, size_t max_read_len, size_t *str_len) {
263 AWS_ERROR_PRECONDITION(str && str_len, AWS_ERROR_INVALID_ARGUMENT);
264
265 /* why not strnlen? It doesn't work everywhere as it wasn't standardized til C11, and is considered
266 * a GNU extension. This should be faster anyways. This should work for ascii and utf8.
267 * Any other character sets in use deserve what they get. */
268 char *null_char_ptr = memchr(str, '\0', max_read_len);
269
270 if (null_char_ptr) {
271 *str_len = null_char_ptr - str;
272 return AWS_OP_SUCCESS;
273 }
274
275 return aws_raise_error(AWS_ERROR_C_STRING_BUFFER_NOT_NULL_TERMINATED);
276}
277