Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package aws-c-common for openSUSE:Factory checked in at 2026-07-08 17:39:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/aws-c-common (Old) and /work/SRC/openSUSE:Factory/.aws-c-common.new.1982 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "aws-c-common" Wed Jul 8 17:39:25 2026 rev:32 rq:1364498 version:0.14.1 Changes: -------- --- /work/SRC/openSUSE:Factory/aws-c-common/aws-c-common.changes 2026-06-15 19:53:13.424023743 +0200 +++ /work/SRC/openSUSE:Factory/.aws-c-common.new.1982/aws-c-common.changes 2026-07-08 17:42:17.101488047 +0200 @@ -1,0 +2,7 @@ +Fri Jul 3 12:47:55 UTC 2026 - John Paul Adrian Glaubitz <[email protected]> + +- Update to 0.14.1 + * odirect write support by @TingDaoK in (#1245) + * aws_cbor_decoder_get_unconsumed_length by @TingDaoK in (#1251) + +------------------------------------------------------------------- Old: ---- v0.14.0.tar.gz New: ---- v0.14.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ aws-c-common.spec ++++++ --- /var/tmp/diff_new_pack.rR5bYL/_old 2026-07-08 17:42:19.009554538 +0200 +++ /var/tmp/diff_new_pack.rR5bYL/_new 2026-07-08 17:42:19.037555514 +0200 @@ -19,7 +19,7 @@ %define library_version 1.0.0 %define library_soversion 1 Name: aws-c-common -Version: 0.14.0 +Version: 0.14.1 Release: 0 Summary: Core C99 package for AWS SDK for C License: Apache-2.0 ++++++ v0.14.0.tar.gz -> v0.14.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-common-0.14.0/include/aws/common/cbor.h new/aws-c-common-0.14.1/include/aws/common/cbor.h --- old/aws-c-common-0.14.0/include/aws/common/cbor.h 2026-05-28 00:02:05.000000000 +0200 +++ new/aws-c-common-0.14.1/include/aws/common/cbor.h 2026-06-24 19:58:50.000000000 +0200 @@ -305,6 +305,8 @@ /** * @brief Get the length of the remaining bytes of the source. Once the source was decoded, it will be consumed, * and result in decrease of the remaining length of bytes. + * Note: aws_cbor_decoder_peek_type will also decrease the remaining length, as it decodes the next element + * internally. * * @param decoder * @return The length of bytes remaining of the decoder source. @@ -313,6 +315,17 @@ size_t aws_cbor_decoder_get_remaining_length(const struct aws_cbor_decoder *decoder); /** + * @brief Get the number of bytes that have not yet been consumed (popped) by the caller. + * Unlike aws_cbor_decoder_get_remaining_length, aws_cbor_decoder_peek_type does NOT affect this value. + * Only pop/consume operations reduce the unconsumed length. + * + * @param decoder + * @return The number of bytes not yet consumed from the decoder source. + */ +AWS_COMMON_API +size_t aws_cbor_decoder_get_unconsumed_length(const struct aws_cbor_decoder *decoder); + +/** * @brief Reset the decoder source to a new src. * The previous src will be discarded regardless of the unconsumed bytes. * The decoder will clear its cache if any. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-common-0.14.0/include/aws/common/file.h new/aws-c-common-0.14.1/include/aws/common/file.h --- old/aws-c-common-0.14.0/include/aws/common/file.h 2026-05-28 00:02:05.000000000 +0200 +++ new/aws-c-common-0.14.1/include/aws/common/file.h 2026-06-24 19:58:50.000000000 +0200 @@ -267,6 +267,47 @@ struct aws_byte_buf *output_buf, size_t *out_actual_read); +/* + * Write to a file using DIRECT I/O at the given offset. + * Using direct IO to bypass the OS cache. Helpful when the disk I/O outperform the kernel cache. + * If O_DIRECT is not supported, returns AWS_ERROR_UNSUPPORTED_OPERATION. + * + * The file must already exist; the caller is responsible for creating it. + * + * For aligned writes (offset and length both aligned to page size), O_DIRECT is used for the + * entire write. + * + * Notes: + * - ONLY supports linux for now and raises AWS_ERROR_UNSUPPORTED_OPERATION on all other platforms. + * - The offset, data.len, and data.ptr all need to be aligned with the page size (a multiple of page size). + * Otherwise, AWS_ERROR_INVALID_ARGUMENT will be raised. + * - check the NOTES for O_DIRECT in https://man7.org/linux/man-pages/man2/openat.2.html + * + * @param file_path The file path to write to. + * @param offset The offset in the file to start writing at. + * @param data The buffer to write from (data.len bytes will be written). + * + * Returns AWS_OP_SUCCESS, or AWS_OP_ERR (after an error has been raised). + */ +AWS_COMMON_API +int aws_file_path_write_to_offset_direct_io( + const struct aws_string *file_path, + uint64_t offset, + struct aws_byte_cursor data); + +/** + * Returns true if direct I/O (O_DIRECT) is supported on the current platform. + * + * Currently only Linux supports direct I/O. On unsupported platforms, + * aws_file_path_read_from_offset_direct_io() and aws_file_path_write_to_offset_direct_io() + * will raise AWS_ERROR_UNSUPPORTED_OPERATION. + * + * Use this to check at init time whether direct I/O is viable, rather than calling the + * read/write functions and handling the error reactively. + */ +AWS_COMMON_API +bool aws_file_direct_io_is_supported(void); + AWS_EXTERN_C_END AWS_POP_SANE_WARNING_LEVEL #endif /* AWS_COMMON_FILE_H */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-common-0.14.0/source/cbor.c new/aws-c-common-0.14.1/source/cbor.c --- old/aws-c-common-0.14.0/source/cbor.c 2026-05-28 00:02:05.000000000 +0200 +++ new/aws-c-common-0.14.1/source/cbor.c 2026-06-24 19:58:50.000000000 +0200 @@ -302,6 +302,9 @@ struct aws_cbor_decoder_context cached_context; + /* Number of bytes from src consumed by peek (cached but not yet popped) */ + size_t cached_bytes_consumed; + /* Error code during decoding. Fail the decoding process without recovering, */ int error_code; @@ -327,6 +330,15 @@ return decoder->src.len; } +size_t aws_cbor_decoder_get_unconsumed_length(const struct aws_cbor_decoder *decoder) { + if (decoder->cached_context.type != AWS_CBOR_TYPE_UNKNOWN) { + /* peek_type decodes the next element and advances src, but from the caller's perspective + * those bytes haven't been consumed yet (the item hasn't been popped). Add them back. */ + return decoder->src.len + decoder->cached_bytes_consumed; + } + return decoder->src.len; +} + void aws_cbor_decoder_reset_src(struct aws_cbor_decoder *decoder, struct aws_byte_cursor src) { decoder->src = src; /* Reset all cached state */ @@ -506,6 +518,7 @@ } aws_byte_cursor_advance(&decoder->src, result.read); + decoder->cached_bytes_consumed = result.read; return AWS_OP_SUCCESS; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-common-0.14.0/source/linux/file_direct_io.c new/aws-c-common-0.14.1/source/linux/file_direct_io.c --- old/aws-c-common-0.14.0/source/linux/file_direct_io.c 2026-05-28 00:02:05.000000000 +0200 +++ new/aws-c-common-0.14.1/source/linux/file_direct_io.c 2026-06-24 19:58:50.000000000 +0200 @@ -9,9 +9,11 @@ #include <aws/common/file.h> #include <aws/common/logging.h> #include <aws/common/string.h> +#include <aws/common/system_info.h> #include <dirent.h> #include <errno.h> #include <fcntl.h> +#include <inttypes.h> #include <pwd.h> #include <stdio.h> #include <sys/stat.h> @@ -52,6 +54,24 @@ return AWS_OP_SUCCESS; /* Nothing to do. */ } + size_t page_size = aws_system_info_page_size(); + + /* Per open(2), O_DIRECT may impose alignment restrictions on the file offset, buffer + * pointer, and transfer length. Misaligned I/Os can either fail with EINVAL or silently + * fall back to buffered I/O depending on the filesystem and kernel version. We enforce + * alignment here to guarantee consistent, predictable behavior. */ + /* Length is not forced for the case of reading last part to end of stream cannot align. */ + if (offset % page_size != 0 || (uintptr_t)(output_buf->buffer + output_buf->len) % page_size != 0) { + AWS_LOGF_ERROR( + AWS_LS_COMMON_GENERAL, + "aws_file_path_read_from_offset_direct_io: offset %" PRIu64 + ", buffer pointer %p must all be aligned to page size %zu", + offset, + (void *)(output_buf->buffer + output_buf->len), + page_size); + return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + } + int rt_code = AWS_OP_ERR; int fd = open(aws_string_c_str(file_path), O_RDONLY | O_DIRECT); if (fd == -1) { @@ -139,3 +159,94 @@ return aws_file_path_read_from_offset_direct_io_with_chunk_size( file_path, offset, max_read_length, AWS_FILE_MAX_READ_CHUNK, output_buf, out_actual_read); } + +int aws_file_path_write_to_offset_direct_io( + const struct aws_string *file_path, + uint64_t offset, + struct aws_byte_cursor data) { + + if (O_DIRECT == 0) { + AWS_LOGF_ERROR(AWS_LS_COMMON_GENERAL, "O_DIRECT is not supported on this platform"); + return aws_raise_error(AWS_ERROR_UNSUPPORTED_OPERATION); + } + + if (data.len == 0) { + return AWS_OP_SUCCESS; + } + + size_t page_size = aws_system_info_page_size(); + + /* Per open(2), O_DIRECT may impose alignment restrictions on the file offset, buffer + * pointer, and transfer length. Misaligned I/Os can either fail with EINVAL or silently + * fall back to buffered I/O depending on the filesystem and kernel version. We enforce + * alignment here to guarantee consistent, predictable behavior. */ + if (offset % page_size != 0 || (uintptr_t)data.ptr % page_size != 0 || data.len % page_size != 0) { + AWS_LOGF_ERROR( + AWS_LS_COMMON_GENERAL, + "aws_file_path_write_to_offset_direct_io: offset %" PRIu64 + ", buffer pointer %p, and data.len %zu must all be aligned to page size %zu", + offset, + (void *)data.ptr, + data.len, + page_size); + return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + } + + int rt_code = AWS_OP_ERR; + int fd = open(aws_string_c_str(file_path), O_WRONLY | O_DIRECT); + if (fd == -1) { + int errno_value = errno; + AWS_LOGF_ERROR( + AWS_LS_COMMON_GENERAL, + "Failed to open file %s for writing with O_DIRECT, errno: %d", + aws_string_c_str(file_path), + errno_value); + aws_translate_and_raise_io_error(errno_value); + goto cleanup; + } + + if (lseek(fd, (off_t)offset, SEEK_SET) == -1) { + int errno_value = errno; + AWS_LOGF_ERROR( + AWS_LS_COMMON_GENERAL, + "Failed to seek to position %llu in file %s, errno: %d", + (unsigned long long)offset, + aws_string_c_str(file_path), + errno_value); + aws_translate_and_raise_io_error(errno_value); + goto cleanup; + } + + /* Write with O_DIRECT */ + size_t total_written = 0; + while (total_written < data.len) { + size_t chunk_size = aws_min_size(data.len - total_written, AWS_FILE_MAX_READ_CHUNK); + ssize_t written = write(fd, data.ptr + total_written, chunk_size); + if (written == -1) { + int errno_value = errno; + AWS_LOGF_ERROR( + AWS_LS_COMMON_GENERAL, + "Failed to write %zu bytes to file %s with O_DIRECT, errno: %d", + chunk_size, + aws_string_c_str(file_path), + errno_value); + aws_translate_and_raise_io_error(errno_value); + goto cleanup; + } + total_written += (size_t)written; + } + + rt_code = AWS_OP_SUCCESS; +cleanup: + if (fd != -1) { + close(fd); + } + return rt_code; +} + +bool aws_file_direct_io_is_supported(void) { + /* O_DIRECT may be defined as 0 in some Linux build environments (e.g. glibc that doesn't + * expose it). When that's the case, opening with O_DIRECT is a no-op and direct I/O is + * not actually available. */ + return O_DIRECT != 0; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-common-0.14.0/source/platform_fallback_stubs/file_direct_io.c new/aws-c-common-0.14.1/source/platform_fallback_stubs/file_direct_io.c --- old/aws-c-common-0.14.0/source/platform_fallback_stubs/file_direct_io.c 2026-05-28 00:02:05.000000000 +0200 +++ new/aws-c-common-0.14.1/source/platform_fallback_stubs/file_direct_io.c 2026-06-24 19:58:50.000000000 +0200 @@ -22,3 +22,18 @@ AWS_LOGF_ERROR(AWS_LS_COMMON_GENERAL, "Direct file IO is not supported yet on platforms other than linux."); return aws_raise_error(AWS_ERROR_UNSUPPORTED_OPERATION); } + +int aws_file_path_write_to_offset_direct_io( + const struct aws_string *file_path, + uint64_t offset, + struct aws_byte_cursor data) { + (void)file_path; + (void)offset; + (void)data; + AWS_LOGF_ERROR(AWS_LS_COMMON_GENERAL, "Direct file IO is not supported yet on platforms other than linux."); + return aws_raise_error(AWS_ERROR_UNSUPPORTED_OPERATION); +} + +bool aws_file_direct_io_is_supported(void) { + return false; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-common-0.14.0/tests/CMakeLists.txt new/aws-c-common-0.14.1/tests/CMakeLists.txt --- old/aws-c-common-0.14.0/tests/CMakeLists.txt 2026-05-28 00:02:05.000000000 +0200 +++ new/aws-c-common-0.14.1/tests/CMakeLists.txt 2026-06-24 19:58:50.000000000 +0200 @@ -521,6 +521,7 @@ add_test_case(test_file_path_read_from_offset_direct_io) add_test_case(test_file_path_read_from_offset) add_test_case(test_file_path_read_from_offset_direct_io_chunking) +add_test_case(test_file_path_write_to_offset_direct_io) add_test_case(test_json_parse_from_string) add_test_case(test_json_parse_to_string) @@ -569,6 +570,7 @@ add_test_case(cbor_decode_error_handling_test) add_test_case(cbor_decode_resource_limit_container_size_test) add_test_case(cbor_decode_resource_limit_depth_test) +add_test_case(cbor_decode_remaining_length_after_peek_test) generate_test_driver(${PROJECT_NAME}-tests) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-common-0.14.0/tests/cbor_test.c new/aws-c-common-0.14.1/tests/cbor_test.c --- old/aws-c-common-0.14.0/tests/cbor_test.c 2026-05-28 00:02:05.000000000 +0200 +++ new/aws-c-common-0.14.1/tests/cbor_test.c 2026-06-24 19:58:50.000000000 +0200 @@ -549,3 +549,49 @@ aws_common_library_clean_up(); return AWS_OP_SUCCESS; } + +CBOR_TEST_CASE(cbor_decode_remaining_length_after_peek_test) { + (void)allocator; + (void)ctx; + aws_common_library_init(allocator); + + struct aws_cbor_encoder *encoder = aws_cbor_encoder_new(allocator); + aws_cbor_encoder_write_uint(encoder, 42); + aws_cbor_encoder_write_uint(encoder, 100); + struct aws_byte_cursor cursor = aws_cbor_encoder_get_encoded_data(encoder); + size_t total_len = cursor.len; + + struct aws_cbor_decoder *decoder = aws_cbor_decoder_new(allocator, cursor); + ASSERT_UINT_EQUALS(total_len, aws_cbor_decoder_get_remaining_length(decoder)); + ASSERT_UINT_EQUALS(total_len, aws_cbor_decoder_get_unconsumed_length(decoder)); + + /* peek_type: old API (remaining_length) DOES decrease, new API (unconsumed_length) does NOT */ + enum aws_cbor_type out_type; + ASSERT_SUCCESS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_TRUE(aws_cbor_decoder_get_remaining_length(decoder) < total_len); + ASSERT_UINT_EQUALS(total_len, aws_cbor_decoder_get_unconsumed_length(decoder)); + + /* after pop, both decrease */ + uint64_t val; + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_unsigned_int_val(decoder, &val)); + ASSERT_UINT_EQUALS(42, val); + size_t after_first_pop = aws_cbor_decoder_get_unconsumed_length(decoder); + ASSERT_TRUE(after_first_pop < total_len); + ASSERT_UINT_EQUALS(after_first_pop, aws_cbor_decoder_get_remaining_length(decoder)); + + /* peek second element: unconsumed_length unchanged, remaining_length decreases */ + ASSERT_SUCCESS(aws_cbor_decoder_peek_type(decoder, &out_type)); + ASSERT_UINT_EQUALS(after_first_pop, aws_cbor_decoder_get_unconsumed_length(decoder)); + ASSERT_TRUE(aws_cbor_decoder_get_remaining_length(decoder) < after_first_pop); + + /* pop second, both should be 0 */ + ASSERT_SUCCESS(aws_cbor_decoder_pop_next_unsigned_int_val(decoder, &val)); + ASSERT_UINT_EQUALS(100, val); + ASSERT_UINT_EQUALS(0, aws_cbor_decoder_get_remaining_length(decoder)); + ASSERT_UINT_EQUALS(0, aws_cbor_decoder_get_unconsumed_length(decoder)); + + aws_cbor_encoder_destroy(encoder); + aws_cbor_decoder_destroy(decoder); + aws_common_library_clean_up(); + return AWS_OP_SUCCESS; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-common-0.14.0/tests/file_test.c new/aws-c-common-0.14.1/tests/file_test.c --- old/aws-c-common-0.14.0/tests/file_test.c 2026-05-28 00:02:05.000000000 +0200 +++ new/aws-c-common-0.14.1/tests/file_test.c 2026-06-24 19:58:50.000000000 +0200 @@ -11,8 +11,6 @@ #include <aws/testing/aws_test_harness.h> -#include <fcntl.h> - static int s_aws_fopen_test_helper(char *file_path, char *content) { char read_result[100]; AWS_ZERO_ARRAY(read_result); @@ -601,7 +599,7 @@ static int s_test_file_path_read_from_offset_direct_io(struct aws_allocator *allocator, void *ctx) { (void)ctx; -#if defined(__linux__) +#if defined(AWS_OS_LINUX) struct aws_file_path_read_from_offset_tester tester; char file_path[] = "test_file_path_read_from_offset_direct_io.txt"; @@ -805,7 +803,7 @@ static int s_test_file_path_read_from_offset_direct_io_chunking(struct aws_allocator *allocator, void *ctx) { (void)ctx; -#if defined(__linux__) +#if defined(AWS_OS_LINUX) struct aws_file_path_read_from_offset_tester tester; char file_path[] = "test_direct_io_chunking.txt"; /* Instead of creating a 2GiB file to test, we use the separate API that allows us to pass in the chunk size. */ @@ -895,3 +893,148 @@ } AWS_TEST_CASE(test_file_path_read_from_offset_direct_io_chunking, s_test_file_path_read_from_offset_direct_io_chunking) + +static int s_test_file_path_write_to_offset_direct_io(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + +#if defined(AWS_OS_LINUX) + size_t page_size = aws_system_info_page_size(); + struct aws_allocator *aligned_alloc = aws_explicit_aligned_allocator_new(page_size); + ASSERT_NOT_NULL(aligned_alloc); + + char file_path_cstr[] = "test_file_path_write_to_offset_direct_io.txt"; + struct aws_string *file_path = aws_string_new_from_c_str(allocator, file_path_cstr); + + /* Create the file first (function requires it to exist) */ + FILE *f = aws_fopen(file_path_cstr, "wb"); + ASSERT_NOT_NULL(f); + fclose(f); + + /* Test 1: Write 2 aligned pages of 'A' at offset 0 */ + size_t two_pages = page_size * 2; + struct aws_byte_buf write_buf; + ASSERT_SUCCESS(aws_byte_buf_init(&write_buf, aligned_alloc, two_pages)); + memset(write_buf.buffer, 'A', two_pages); + write_buf.len = two_pages; + struct aws_byte_cursor write_cursor = aws_byte_cursor_from_buf(&write_buf); + ASSERT_SUCCESS(aws_file_path_write_to_offset_direct_io(file_path, 0, write_cursor)); + + /* Read back and verify */ + struct aws_byte_buf read_buf; + ASSERT_SUCCESS(aws_byte_buf_init(&read_buf, aligned_alloc, two_pages)); + size_t actual_read = 0; + ASSERT_SUCCESS(aws_file_path_read_from_offset_direct_io(file_path, 0, two_pages, &read_buf, &actual_read)); + ASSERT_UINT_EQUALS(two_pages, actual_read); + for (size_t i = 0; i < two_pages; i++) { + ASSERT_UINT_EQUALS('A', read_buf.buffer[i]); + } + aws_byte_buf_clean_up(&read_buf); + + /* Test 2: Write 1 page of 'C' at page-aligned offset (second page) */ + struct aws_byte_buf overwrite_buf; + ASSERT_SUCCESS(aws_byte_buf_init(&overwrite_buf, aligned_alloc, page_size)); + memset(overwrite_buf.buffer, 'C', page_size); + overwrite_buf.len = page_size; + struct aws_byte_cursor overwrite_cursor = aws_byte_cursor_from_buf(&overwrite_buf); + ASSERT_SUCCESS(aws_file_path_write_to_offset_direct_io(file_path, (uint64_t)page_size, overwrite_cursor)); + + /* Read back and verify first page still 'A', second page now 'C' */ + ASSERT_SUCCESS(aws_byte_buf_init(&read_buf, aligned_alloc, two_pages)); + actual_read = 0; + ASSERT_SUCCESS(aws_file_path_read_from_offset_direct_io(file_path, 0, two_pages, &read_buf, &actual_read)); + ASSERT_UINT_EQUALS(two_pages, actual_read); + for (size_t i = 0; i < page_size; i++) { + ASSERT_UINT_EQUALS('A', read_buf.buffer[i]); + } + for (size_t i = page_size; i < two_pages; i++) { + ASSERT_UINT_EQUALS('C', read_buf.buffer[i]); + } + aws_byte_buf_clean_up(&read_buf); + aws_byte_buf_clean_up(&overwrite_buf); + + /* Test 3: Unaligned MUST fail */ + struct aws_byte_buf unaligned_buf; + size_t unaligned_len = page_size + 37; + ASSERT_SUCCESS(aws_byte_buf_init(&unaligned_buf, aligned_alloc, unaligned_len)); + memset(unaligned_buf.buffer, 'X', unaligned_len); + unaligned_buf.len = unaligned_len; + struct aws_byte_cursor unaligned_cursor = aws_byte_cursor_from_buf(&unaligned_buf); + ASSERT_FAILS(aws_file_path_write_to_offset_direct_io(file_path, 37, unaligned_cursor)); + ASSERT_UINT_EQUALS(AWS_ERROR_INVALID_ARGUMENT, aws_last_error()); + aws_byte_buf_clean_up(&unaligned_buf); + + /* Test 4: File does not exist MUST fail */ + struct aws_string *nonexistent = aws_string_new_from_c_str(allocator, "nonexistent_direct_io_file.txt"); + struct aws_byte_cursor one_page_cursor = {.ptr = write_buf.buffer, .len = page_size}; + ASSERT_FAILS(aws_file_path_write_to_offset_direct_io(nonexistent, 0, one_page_cursor)); + aws_string_destroy(nonexistent); + + /* Test 5: Mixed O_DIRECT + buffered writes at non-overlapping offsets, verify all data correct. + * Simulates the download pattern: O_DIRECT for aligned parts, buffered write for last part tail. */ + remove(file_path_cstr); + f = aws_fopen(file_path_cstr, "wb"); + ASSERT_NOT_NULL(f); + fclose(f); + + /* Write 3 pages with O_DIRECT: page 0='D', page 1='E', page 2='F' */ + memset(write_buf.buffer, 'D', page_size); + write_cursor = (struct aws_byte_cursor){.ptr = write_buf.buffer, .len = page_size}; + ASSERT_SUCCESS(aws_file_path_write_to_offset_direct_io(file_path, 0, write_cursor)); + + memset(write_buf.buffer, 'E', page_size); + ASSERT_SUCCESS(aws_file_path_write_to_offset_direct_io(file_path, (uint64_t)page_size, write_cursor)); + + memset(write_buf.buffer, 'F', page_size); + ASSERT_SUCCESS(aws_file_path_write_to_offset_direct_io(file_path, (uint64_t)(page_size * 2), write_cursor)); + + /* Write unaligned tail (37 bytes of 'G') at offset page_size*3 using buffered write */ + size_t tail_len = 37; + uint8_t tail_data[37]; + memset(tail_data, 'G', tail_len); + FILE *tail_file = aws_fopen(file_path_cstr, "r+b"); + ASSERT_NOT_NULL(tail_file); + ASSERT_TRUE(fseek(tail_file, (long)(page_size * 3), SEEK_SET) == 0); + size_t written = fwrite(tail_data, 1, tail_len, tail_file); + ASSERT_INT_EQUALS((int)tail_len, (int)written); + fclose(tail_file); + + /* Read entire file back and verify all regions */ + size_t total_size = page_size * 3 + tail_len; + FILE *verify_file = aws_fopen(file_path_cstr, "rb"); + ASSERT_NOT_NULL(verify_file); + uint8_t *verify_buf = aws_mem_calloc(allocator, 1, total_size); + ASSERT_UINT_EQUALS(total_size, fread(verify_buf, 1, total_size, verify_file)); + fclose(verify_file); + + for (size_t i = 0; i < page_size; i++) { + ASSERT_UINT_EQUALS('D', verify_buf[i]); + } + for (size_t i = page_size; i < page_size * 2; i++) { + ASSERT_UINT_EQUALS('E', verify_buf[i]); + } + for (size_t i = page_size * 2; i < page_size * 3; i++) { + ASSERT_UINT_EQUALS('F', verify_buf[i]); + } + for (size_t i = page_size * 3; i < total_size; i++) { + ASSERT_UINT_EQUALS('G', verify_buf[i]); + } + aws_mem_release(allocator, verify_buf); + + /* Cleanup */ + aws_byte_buf_clean_up(&write_buf); + remove(file_path_cstr); + aws_string_destroy(file_path); + aws_explicit_aligned_allocator_destroy(aligned_alloc); +#else + (void)allocator; + struct aws_string *file_path = aws_string_new_from_c_str(allocator, "dummy.txt"); + struct aws_byte_cursor empty = {.ptr = NULL, .len = 0}; + ASSERT_FAILS(aws_file_path_write_to_offset_direct_io(file_path, 0, empty)); + ASSERT_UINT_EQUALS(AWS_ERROR_UNSUPPORTED_OPERATION, aws_last_error()); + aws_string_destroy(file_path); +#endif + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(test_file_path_write_to_offset_direct_io, s_test_file_path_write_to_offset_direct_io)
