Skip to content

Commit

Permalink
More byte_buf.h functions for HTTP/2 (#581)
Browse files Browse the repository at this point in the history
Better way to read/write an integer with 3 bytes (HTTP/2 encodes length this way).
- DELETE `aws_hton24()` and `aws_ntoh24()`.
  - These had bugs in the big-endian implementation, and it was hard to understand how to use them correctly.
- ADD `aws_byte_cursor_read_be24()` reads 3 bytes into a u32.
- ADD `aws_byte_buffer_write_be24()` writes "low" 3 bytes of u32.

Better way to write padding zeroes.
- ADD `aws_byte_buf_write_u8_n()` which is like memset()

Semi-related, `ASSERT_BIN_ARRAYS_EQUALS()` prints index & value of 1st mismatched byte.
  • Loading branch information
graebm authored Jan 29, 2020
1 parent b0ea9f3 commit e5c9e3c
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 69 deletions.
65 changes: 47 additions & 18 deletions include/aws/common/byte_buf.h
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,17 @@ AWS_COMMON_API bool aws_byte_cursor_read_u8(struct aws_byte_cursor *AWS_RESTRICT
*/
AWS_COMMON_API bool aws_byte_cursor_read_be16(struct aws_byte_cursor *cur, uint16_t *var);

/**
* Reads an unsigned 24-bit value (3 bytes) in network byte order from cur,
* and places it in host byte order into 32-bit var.
* Ex: if cur's next 3 bytes are {0xAA, 0xBB, 0xCC}, then var becomes 0x00AABBCC.
*
* On success, returns true and updates the cursor pointer/length accordingly.
* If there is insufficient space in the cursor, returns false, leaving the
* cursor unchanged.
*/
AWS_COMMON_API bool aws_byte_cursor_read_be24(struct aws_byte_cursor *cur, uint32_t *var);

/**
* Reads a 32-bit value in network byte order from cur, and places it in host
* byte order into var.
Expand Down Expand Up @@ -668,54 +679,72 @@ AWS_COMMON_API bool aws_byte_buf_write_from_whole_cursor(
*
* On success, returns true and updates the cursor /length
accordingly.
* If there is insufficient space in the cursor, returns false, leaving the
cursor unchanged.
*
* If there is insufficient space in the buffer, returns false, leaving the
* buffer unchanged.
*/
AWS_COMMON_API bool aws_byte_buf_write_u8(struct aws_byte_buf *AWS_RESTRICT buf, uint8_t c);

/**
* Writes one byte repeatedly to buffer (like memset)
*
* If there is insufficient space in the buffer, returns false, leaving the
* buffer unchanged.
*/
AWS_COMMON_API bool aws_byte_buf_write_u8_n(struct aws_byte_buf *buf, uint8_t c, size_t count);

/**
* Writes a 16-bit integer in network byte order (big endian) to buffer.
*
* On success, returns true and updates the cursor /length accordingly.
* If there is insufficient space in the cursor, returns false, leaving the
* cursor unchanged.
* On success, returns true and updates the buffer /length accordingly.
* If there is insufficient space in the buffer, returns false, leaving the
* buffer unchanged.
*/
AWS_COMMON_API bool aws_byte_buf_write_be16(struct aws_byte_buf *buf, uint16_t x);

/**
* Writes low 24-bits (3 bytes) of an unsigned integer in network byte order (big endian) to buffer.
* Ex: If x is 0x00AABBCC then {0xAA, 0xBB, 0xCC} is written to buffer.
*
* On success, returns true and updates the buffer /length accordingly.
* If there is insufficient space in the buffer, or x's value cannot fit in 3 bytes,
* returns false, leaving the buffer unchanged.
*/
AWS_COMMON_API bool aws_byte_buf_write_be24(struct aws_byte_buf *buf, uint32_t x);

/**
* Writes a 32-bit integer in network byte order (big endian) to buffer.
*
* On success, returns true and updates the cursor /length accordingly.
* If there is insufficient space in the cursor, returns false, leaving the
* cursor unchanged.
* On success, returns true and updates the buffer /length accordingly.
* If there is insufficient space in the buffer, returns false, leaving the
* buffer unchanged.
*/
AWS_COMMON_API bool aws_byte_buf_write_be32(struct aws_byte_buf *buf, uint32_t x);

/**
* Writes a 32-bit float in network byte order (big endian) to buffer.
*
* On success, returns true and updates the cursor /length accordingly.
* If there is insufficient space in the cursor, returns false, leaving the
* cursor unchanged.
* On success, returns true and updates the buffer /length accordingly.
* If there is insufficient space in the buffer, returns false, leaving the
* buffer unchanged.
*/
AWS_COMMON_API bool aws_byte_buf_write_float_be32(struct aws_byte_buf *buf, float x);

/**
* Writes a 64-bit integer in network byte order (big endian) to buffer.
*
* On success, returns true and updates the cursor /length accordingly.
* If there is insufficient space in the cursor, returns false, leaving the
* cursor unchanged.
* On success, returns true and updates the buffer /length accordingly.
* If there is insufficient space in the buffer, returns false, leaving the
* buffer unchanged.
*/
AWS_COMMON_API bool aws_byte_buf_write_be64(struct aws_byte_buf *buf, uint64_t x);

/**
* Writes a 64-bit float in network byte order (big endian) to buffer.
*
* On success, returns true and updates the cursor /length accordingly.
* If there is insufficient space in the cursor, returns false, leaving the
* cursor unchanged.
* On success, returns true and updates the buffer /length accordingly.
* If there is insufficient space in the buffer, returns false, leaving the
* buffer unchanged.
*/
AWS_COMMON_API bool aws_byte_buf_write_float_be64(struct aws_byte_buf *buf, double x);

Expand Down
10 changes: 0 additions & 10 deletions include/aws/common/byte_order.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,6 @@ AWS_STATIC_IMPL float aws_ntohf32(float x);
*/
AWS_STATIC_IMPL double aws_ntohf64(double x);

/**
* Convert 24 bit integer from host to network byte order.
*/
AWS_STATIC_IMPL uint32_t aws_hton24(uint32_t x);

/**
* Convert 24 bit integer from network to host byte order.
*/
AWS_STATIC_IMPL uint32_t aws_ntoh24(uint32_t x);

/**
* Convert 16 bit integer from host to network byte order.
*/
Expand Down
24 changes: 0 additions & 24 deletions include/aws/common/byte_order.inl
Original file line number Diff line number Diff line change
Expand Up @@ -147,30 +147,6 @@ AWS_STATIC_IMPL double aws_ntohf64(double x) {
return aws_htonf64(x);
}

/**
* Convert 24 bit integer from host to network byte order.
*/
AWS_STATIC_IMPL uint32_t aws_hton24(uint32_t x) {
AWS_PRECONDITION(x <= 0xFFFFFF, "Input [x] must be representable with at most 3 bytes.");
if (aws_is_big_endian()) {
return x;
} else {
return aws_hton32(x) >> 8;
}
}

/**
* Convert 24 bit integer from network to host byte order.
*/
AWS_STATIC_IMPL uint32_t aws_ntoh24(uint32_t x) {
AWS_PRECONDITION((x) <= 0xFFFFFFF, "Input [x] must be representable with at most 3 bytes.");
if (aws_is_big_endian()) {
return x;
} else {
return aws_ntoh32(x) >> 8;
}
}

/**
* Convert 16 bit integer from host to network byte order.
*/
Expand Down
17 changes: 16 additions & 1 deletion include/aws/testing/aws_test_harness.h
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,22 @@ static int total_failures;
POSTFAIL_INTERNAL(); \
} \
if (memcmp(assert_ex_p, assert_got_p, assert_got_s) != 0) { \
fprintf(AWS_TESTING_REPORT_FD, "%sData mismatch: ", FAIL_PREFIX); \
if (assert_got_s <= 1024) { \
for (size_t assert_i = 0; assert_i < assert_ex_s; ++assert_i) { \
if (assert_ex_p[assert_i] != assert_got_p[assert_i]) { \
fprintf( \
AWS_TESTING_REPORT_FD, \
"%sMismatch at byte[%zu]: 0x%02X != 0x%02X: ", \
FAIL_PREFIX, \
assert_i, \
assert_ex_p[assert_i], \
assert_got_p[assert_i]); \
break; \
} \
} \
} else { \
fprintf(AWS_TESTING_REPORT_FD, "%sData mismatch: ", FAIL_PREFIX); \
} \
if (!PRINT_FAIL_INTERNAL0(__VA_ARGS__)) { \
PRINT_FAIL_INTERNAL0( \
"ASSERT_BIN_ARRAYS_EQUALS(%s, %s, %s, %s)", #expected, #expected_size, #got, #got_size); \
Expand Down
74 changes: 73 additions & 1 deletion source/byte_buf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,35 @@ bool aws_byte_cursor_read_be16(struct aws_byte_cursor *cur, uint16_t *var) {
return rv;
}

/**
* Reads an unsigned 24-bit value (3 bytes) in network byte order from cur,
* and places it in host byte order into 32-bit var.
* Ex: if cur's next 3 bytes are {0xAA, 0xBB, 0xCC}, then var becomes 0x00AABBCC.
*
* On success, returns true and updates the cursor pointer/length accordingly.
* If there is insufficient space in the cursor, returns false, leaving the
* cursor unchanged.
*/
bool aws_byte_cursor_read_be24(struct aws_byte_cursor *cur, uint32_t *var) {
AWS_PRECONDITION(aws_byte_cursor_is_valid(cur));
AWS_PRECONDITION(AWS_OBJECT_PTR_IS_WRITABLE(var));

uint8_t *var_bytes = (void *)var;

/* read into "lower" 3 bytes */
bool rv = aws_byte_cursor_read(cur, &var_bytes[1], 3);

if (AWS_LIKELY(rv)) {
/* zero out "highest" 4th byte*/
var_bytes[0] = 0;

*var = aws_ntoh32(*var);
}

AWS_POSTCONDITION(aws_byte_cursor_is_valid(cur));
return rv;
}

/**
* Reads a 32-bit value in network byte order from cur, and places it in host
* byte order into var.
Expand Down Expand Up @@ -1231,7 +1260,7 @@ bool aws_byte_buf_advance(
*/
bool aws_byte_buf_write(struct aws_byte_buf *AWS_RESTRICT buf, const uint8_t *AWS_RESTRICT src, size_t len) {
AWS_PRECONDITION(aws_byte_buf_is_valid(buf));
AWS_PRECONDITION(AWS_MEM_IS_WRITABLE(src, len), "Input array [src] must be readable up to [len] bytes.");
AWS_PRECONDITION(AWS_MEM_IS_READABLE(src, len), "Input array [src] must be readable up to [len] bytes.");

if (buf->len > (SIZE_MAX >> 1) || len > (SIZE_MAX >> 1) || buf->len + len > buf->capacity) {
AWS_POSTCONDITION(aws_byte_buf_is_valid(buf));
Expand Down Expand Up @@ -1285,6 +1314,27 @@ bool aws_byte_buf_write_u8(struct aws_byte_buf *AWS_RESTRICT buf, uint8_t c) {
return aws_byte_buf_write(buf, &c, 1);
}

/**
* Writes one byte repeatedly to buffer (like memset)
*
* If there is insufficient space in the buffer, returns false, leaving the
* buffer unchanged.
*/
bool aws_byte_buf_write_u8_n(struct aws_byte_buf *buf, uint8_t c, size_t count) {
AWS_PRECONDITION(aws_byte_buf_is_valid(buf));

if (buf->len > (SIZE_MAX >> 1) || count > (SIZE_MAX >> 1) || buf->len + count > buf->capacity) {
AWS_POSTCONDITION(aws_byte_buf_is_valid(buf));
return false;
}

memset(buf->buffer + buf->len, c, count);
buf->len += count;

AWS_POSTCONDITION(aws_byte_buf_is_valid(buf));
return true;
}

/**
* Writes a 16-bit integer in network byte order (big endian) to buffer.
*
Expand All @@ -1298,6 +1348,28 @@ bool aws_byte_buf_write_be16(struct aws_byte_buf *buf, uint16_t x) {
return aws_byte_buf_write(buf, (uint8_t *)&x, 2);
}

/**
* Writes low 24-bits (3 bytes) of an unsigned integer in network byte order (big endian) to buffer.
* Ex: If x is 0x00AABBCC then {0xAA, 0xBB, 0xCC} is written to buffer.
*
* On success, returns true and updates the buffer /length accordingly.
* If there is insufficient space in the buffer, or x's value cannot fit in 3 bytes,
* returns false, leaving the buffer unchanged.
*/
bool aws_byte_buf_write_be24(struct aws_byte_buf *buf, uint32_t x) {
AWS_PRECONDITION(aws_byte_buf_is_valid(buf));

if (x > 0x00FFFFFF) {
return false;
}

uint32_t be32 = aws_hton32(x);
uint8_t *be32_bytes = (uint8_t *)&be32;

/* write "lower" 3 bytes */
return aws_byte_buf_write(buf, &be32_bytes[1], 3);
}

/**
* Writes a 32-bit integer in network byte order (big endian) to buffer.
*
Expand Down
12 changes: 0 additions & 12 deletions tests/byte_order_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,38 +27,26 @@ static int s_byte_swap_test_fn(struct aws_allocator *allocator, void *ctx) {

uint64_t ans_x = 0x1122334455667788ULL;
uint32_t ans_y = 0xaabbccdd;
uint32_t ans_z = 0xaabbcc;
uint16_t ans_w = 0xeeff;

uint8_t x[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
uint8_t y[] = {0xaa, 0xbb, 0xcc, 0xdd};
uint8_t z[] = {0x00, 0xaa, 0xbb, 0xcc}; /* Leading 0 needed avoid buffer overflows */
uint8_t w[] = {0xee, 0xff};

uint64_t x64;
uint32_t y32;
uint32_t z24;
uint16_t w16;

memcpy(&x64, x, sizeof(x));
memcpy(&y32, y, sizeof(y));
memcpy(&z24, z, sizeof(z));
memcpy(&w16, w, sizeof(w));

z24 >>= 8; /* Throw away the fake byte */

ASSERT_UINT_EQUALS(aws_ntoh64(x64), ans_x);
ASSERT_UINT_EQUALS(aws_hton64(x64), ans_x);

ASSERT_UINT_EQUALS(aws_ntoh32(y32), ans_y);
ASSERT_UINT_EQUALS(aws_hton32(y32), ans_y);

ASSERT_UINT_EQUALS(aws_ntoh24(z24), ans_z);
ASSERT_UINT_EQUALS(aws_hton24(z24), ans_z);
/* Make sure top byte is untouched */
ASSERT_UINT_EQUALS(aws_ntoh24(z24) & 0xFF000000, 0);
ASSERT_UINT_EQUALS(aws_hton24(z24) & 0xFF000000, 0);

ASSERT_UINT_EQUALS(aws_ntoh16(w16), ans_w);
ASSERT_UINT_EQUALS(aws_hton16(w16), ans_w);

Expand Down
Loading

0 comments on commit e5c9e3c

Please sign in to comment.