Skip to content

Commit 7c73027

Browse files
authored
Hex encoding and byte buf utilities (#328)
* Add secondary hex encode variant that respects current buffer position and doesn't 0-terminate * Add helper byte buf reserve function that is relative to the current length of the buffer
1 parent 219d06f commit 7c73027

File tree

10 files changed

+241
-1
lines changed

10 files changed

+241
-1
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"). You may not use
4+
# this file except in compliance with the License. A copy of the License is
5+
# located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the "license" file accompanying this file. This file is distributed on an
10+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
11+
# implied. See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
14+
###########
15+
CBMC_UNWINDSET =
16+
17+
CBMCFLAGS +=
18+
19+
DEPENDENCIES += $(HELPERDIR)/source/make_common_data_structures.c
20+
DEPENDENCIES += $(HELPERDIR)/source/proof_allocators.c
21+
DEPENDENCIES += $(HELPERDIR)/stubs/error.c
22+
DEPENDENCIES += $(HELPERDIR)/stubs/memcpy_override_no_op.c
23+
DEPENDENCIES += $(HELPERDIR)/stubs/memset_override_no_op.c
24+
DEPENDENCIES += $(SRCDIR)/source/byte_buf.c
25+
DEPENDENCIES += $(SRCDIR)/source/common.c
26+
27+
ENTRY = aws_byte_buf_reserve_relative_harness
28+
###########
29+
30+
include ../Makefile.common
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2019 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+
16+
#include <aws/common/byte_buf.h>
17+
#include <proof_helpers/make_common_data_structures.h>
18+
19+
void aws_byte_buf_reserve_relative_harness() {
20+
struct aws_byte_buf buf;
21+
ensure_byte_buf_has_allocated_buffer_member(&buf);
22+
__CPROVER_assume(aws_byte_buf_is_valid(&buf));
23+
24+
struct aws_byte_buf old = buf;
25+
size_t requested_capacity;
26+
int rval = aws_byte_buf_reserve_relative(&buf, requested_capacity);
27+
28+
if (rval == AWS_OP_SUCCESS) {
29+
assert(buf.capacity >= (old.len + requested_capacity));
30+
}
31+
assert(aws_byte_buf_is_valid(&buf));
32+
assert(is_byte_buf_expected_alloc(&buf));
33+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
jobos: ubuntu16
2+
cbmcflags: "--bounds-check;--div-by-zero-check;--float-overflow-check;--nan-check;--pointer-check;--pointer-overflow-check;--signed-overflow-check;--undefined-shift-check;--unsigned-overflow-check;--unwinding-assertions;--unwind;1"
3+
goto: aws_byte_buf_reserve_relative_harness.goto
4+
expected: "SUCCESSFUL"

include/aws/common/byte_buf.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,16 @@ int aws_byte_buf_append_dynamic(struct aws_byte_buf *to, const struct aws_byte_c
348348
AWS_COMMON_API
349349
int aws_byte_buf_reserve(struct aws_byte_buf *buffer, size_t requested_capacity);
350350

351+
/**
352+
* Convenience function that attempts to increase the capacity of a buffer relative to the current
353+
* length.
354+
*
355+
* aws_byte_buf_reserve_relative(buf, x) ~~ aws_byte_buf_reserve(buf, buf->len + x)
356+
*
357+
*/
358+
AWS_COMMON_API
359+
int aws_byte_buf_reserve_relative(struct aws_byte_buf *buffer, size_t additional_length);
360+
351361
/**
352362
* Concatenates a variable number of struct aws_byte_buf * into destination.
353363
* Number of args must be greater than 1. If dest is too small,

include/aws/common/encoding.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,21 @@ int aws_hex_compute_encoded_len(size_t to_encode_len, size_t *encoded_length);
3434

3535
/*
3636
* Base 16 (hex) encodes the contents of to_encode and stores the result in
37-
* output.
37+
* output. 0 terminates the result. Assumes the buffer is empty and does not resize on
38+
* insufficient capacity.
3839
*/
3940
AWS_COMMON_API
4041
int aws_hex_encode(const struct aws_byte_cursor *AWS_RESTRICT to_encode, struct aws_byte_buf *AWS_RESTRICT output);
4142

43+
/*
44+
* Base 16 (hex) encodes the contents of to_encode and appends the result in
45+
* output. Does not 0-terminate. Grows the destination buffer dynamically if necessary.
46+
*/
47+
AWS_COMMON_API
48+
int aws_hex_encode_append_dynamic(
49+
const struct aws_byte_cursor *AWS_RESTRICT to_encode,
50+
struct aws_byte_buf *AWS_RESTRICT output);
51+
4252
/*
4353
* computes the length necessary to store the result of aws_hex_decode().
4454
* returns -1 on failure, and 0 on success. decoded_len will be set on success.

source/byte_buf.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,17 @@ int aws_byte_buf_reserve(struct aws_byte_buf *buffer, size_t requested_capacity)
539539
return AWS_OP_SUCCESS;
540540
}
541541

542+
int aws_byte_buf_reserve_relative(struct aws_byte_buf *buffer, size_t additional_length) {
543+
AWS_PRECONDITION(aws_byte_buf_is_valid(buffer));
544+
545+
size_t requested_capacity = 0;
546+
if (AWS_UNLIKELY(aws_add_size_checked(buffer->len, additional_length, &requested_capacity))) {
547+
return AWS_OP_ERR;
548+
}
549+
550+
return aws_byte_buf_reserve(buffer, requested_capacity);
551+
}
552+
542553
struct aws_byte_cursor aws_byte_cursor_right_trim_pred(
543554
const struct aws_byte_cursor *source,
544555
aws_byte_predicate_fn *predicate) {

source/encoding.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,33 @@ int aws_hex_encode(const struct aws_byte_cursor *AWS_RESTRICT to_encode, struct
115115
return AWS_OP_SUCCESS;
116116
}
117117

118+
int aws_hex_encode_append_dynamic(
119+
const struct aws_byte_cursor *AWS_RESTRICT to_encode,
120+
struct aws_byte_buf *AWS_RESTRICT output) {
121+
assert(to_encode->ptr);
122+
assert(aws_byte_buf_is_valid(output));
123+
124+
size_t encoded_len = 0;
125+
if (AWS_UNLIKELY(aws_add_size_checked(to_encode->len, to_encode->len, &encoded_len))) {
126+
return AWS_OP_ERR;
127+
}
128+
129+
if (AWS_UNLIKELY(aws_byte_buf_reserve_relative(output, encoded_len))) {
130+
return AWS_OP_ERR;
131+
}
132+
133+
size_t written = output->len;
134+
for (size_t i = 0; i < to_encode->len; ++i) {
135+
136+
output->buffer[written++] = HEX_CHARS[to_encode->ptr[i] >> 4 & 0x0f];
137+
output->buffer[written++] = HEX_CHARS[to_encode->ptr[i] & 0x0f];
138+
}
139+
140+
output->len += encoded_len;
141+
142+
return AWS_OP_SUCCESS;
143+
}
144+
118145
static int s_hex_decode_char_to_int(char character, uint8_t *int_val) {
119146
if (character >= 'a' && character <= 'f') {
120147
*int_val = (uint8_t)(10 + (character - 'a'));

tests/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ add_test_case(hex_encoding_invalid_buffer_size_test)
9595
add_test_case(hex_encoding_highbyte_string_test)
9696
add_test_case(hex_encoding_overflow_test)
9797
add_test_case(hex_encoding_invalid_string_test)
98+
add_test_case(hex_encoding_append_dynamic_test_case_empty)
99+
add_test_case(hex_encoding_append_dynamic_test_case_fooba)
98100
add_test_case(base64_encoding_test_case_empty_test)
99101
add_test_case(base64_encoding_test_case_f_test)
100102
add_test_case(base64_encoding_test_case_fo_test)
@@ -217,6 +219,7 @@ add_test_case(test_byte_buf_append_dynamic)
217219
add_test_case(test_byte_buf_append_lookup_success)
218220
add_test_case(test_byte_buf_append_lookup_failure)
219221
add_test_case(test_byte_buf_reserve)
222+
add_test_case(test_byte_buf_reserve_relative)
220223

221224
add_test_case(byte_swap_test)
222225

tests/byte_buf_test.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,3 +634,27 @@ static int s_test_byte_buf_reserve(struct aws_allocator *allocator, void *ctx) {
634634
return 0;
635635
}
636636
AWS_TEST_CASE(test_byte_buf_reserve, s_test_byte_buf_reserve)
637+
638+
static int s_test_byte_buf_reserve_relative(struct aws_allocator *allocator, void *ctx) {
639+
(void)ctx;
640+
(void)allocator;
641+
642+
struct aws_byte_buf buffer;
643+
aws_byte_buf_init(&buffer, allocator, 1);
644+
645+
struct aws_byte_cursor prefix_cursor = aws_byte_cursor_from_string(s_reserve_test_prefix);
646+
647+
ASSERT_TRUE(aws_byte_buf_reserve_relative(&buffer, prefix_cursor.len) == AWS_OP_SUCCESS);
648+
ASSERT_TRUE(aws_byte_buf_append(&buffer, &prefix_cursor) == AWS_OP_SUCCESS);
649+
650+
struct aws_byte_cursor suffix_cursor = aws_byte_cursor_from_string(s_reserve_test_suffix);
651+
ASSERT_TRUE(aws_byte_buf_reserve_relative(&buffer, suffix_cursor.len) == AWS_OP_SUCCESS);
652+
ASSERT_TRUE(aws_byte_buf_append(&buffer, &suffix_cursor) == AWS_OP_SUCCESS);
653+
654+
ASSERT_TRUE(aws_byte_buf_eq_c_str(&buffer, (char *)s_reserve_test_prefix_concatenated->bytes));
655+
656+
aws_byte_buf_clean_up(&buffer);
657+
658+
return 0;
659+
}
660+
AWS_TEST_CASE(test_byte_buf_reserve_relative, s_test_byte_buf_reserve_relative)

tests/encoding_test.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,17 @@ static int s_hex_encoding_test_case_foobar(struct aws_allocator *allocator, void
168168

169169
AWS_TEST_CASE(hex_encoding_test_case_foobar_test, s_hex_encoding_test_case_foobar)
170170

171+
static int s_hex_encoding_append_test_case(struct aws_allocator *allocator, void *ctx) {
172+
(void)ctx;
173+
174+
char test_data[] = "foobar";
175+
char expected[] = "666f6f626172";
176+
177+
return s_run_hex_encoding_test_case(allocator, test_data, sizeof(test_data), expected, sizeof(expected) - 1);
178+
}
179+
180+
AWS_TEST_CASE(hex_encoding_append_test_case, s_hex_encoding_append_test_case)
181+
171182
static int s_hex_encoding_test_case_missing_leading_zero_fn(struct aws_allocator *allocator, void *ctx) {
172183
(void)allocator;
173184
(void)ctx;
@@ -878,3 +889,80 @@ static int s_uint16_buffer_signed_negative_test_fn(struct aws_allocator *allocat
878889
}
879890

880891
AWS_TEST_CASE(uint16_buffer_signed_negative_test, s_uint16_buffer_signed_negative_test_fn)
892+
893+
static int s_run_hex_encoding_append_dynamic_test_case(
894+
struct aws_allocator *allocator,
895+
const char *test_str,
896+
const char *expected,
897+
size_t initial_capacity,
898+
size_t starting_offset) {
899+
900+
size_t output_size = 2 * strlen(test_str);
901+
902+
struct aws_byte_cursor to_encode = aws_byte_cursor_from_c_str(test_str);
903+
904+
struct aws_byte_buf dest;
905+
ASSERT_SUCCESS(aws_byte_buf_init(&dest, allocator, initial_capacity));
906+
memset(dest.buffer, 0xdd, dest.capacity);
907+
908+
dest.len = starting_offset;
909+
910+
ASSERT_SUCCESS(aws_hex_encode_append_dynamic(&to_encode, &dest), "encode call should have succeeded");
911+
912+
size_t expected_size = strlen(expected);
913+
914+
ASSERT_BIN_ARRAYS_EQUALS(
915+
expected,
916+
expected_size,
917+
dest.buffer + starting_offset,
918+
output_size,
919+
"Encode output should have been {%s}, was {%s}.",
920+
expected,
921+
dest.buffer + starting_offset);
922+
ASSERT_INT_EQUALS(output_size, dest.len - starting_offset);
923+
924+
for (size_t i = 0; i < starting_offset; ++i) {
925+
ASSERT_INT_EQUALS(
926+
(unsigned char)*(dest.buffer + i),
927+
(unsigned char)0xdd,
928+
"Write should not have occurred before the the encoding's starting position.");
929+
}
930+
931+
for (size_t i = starting_offset + output_size; i < dest.capacity; ++i) {
932+
ASSERT_INT_EQUALS(
933+
(unsigned char)*(dest.buffer + i),
934+
(unsigned char)0xdd,
935+
"Write should not have occurred after the encoding's final position.");
936+
}
937+
938+
aws_byte_buf_clean_up(&dest);
939+
return 0;
940+
}
941+
942+
static int s_hex_encoding_append_dynamic_test_case_fooba(struct aws_allocator *allocator, void *ctx) {
943+
(void)ctx;
944+
945+
char test_data[] = "fooba";
946+
char expected[] = "666f6f6261";
947+
948+
ASSERT_TRUE(s_run_hex_encoding_append_dynamic_test_case(allocator, test_data, expected, 5, 3) == AWS_OP_SUCCESS);
949+
ASSERT_TRUE(s_run_hex_encoding_append_dynamic_test_case(allocator, test_data, expected, 50, 3) == AWS_OP_SUCCESS);
950+
951+
return 0;
952+
}
953+
954+
AWS_TEST_CASE(hex_encoding_append_dynamic_test_case_fooba, s_hex_encoding_append_dynamic_test_case_fooba)
955+
956+
static int s_hex_encoding_append_dynamic_test_case_empty(struct aws_allocator *allocator, void *ctx) {
957+
(void)ctx;
958+
959+
char test_data[] = "";
960+
char expected[] = "";
961+
962+
ASSERT_TRUE(s_run_hex_encoding_append_dynamic_test_case(allocator, test_data, expected, 5, 3) == AWS_OP_SUCCESS);
963+
ASSERT_TRUE(s_run_hex_encoding_append_dynamic_test_case(allocator, test_data, expected, 50, 3) == AWS_OP_SUCCESS);
964+
965+
return 0;
966+
}
967+
968+
AWS_TEST_CASE(hex_encoding_append_dynamic_test_case_empty, s_hex_encoding_append_dynamic_test_case_empty)

0 commit comments

Comments
 (0)