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 2025-05-14 17:01:53
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/aws-c-common (Old)
 and      /work/SRC/openSUSE:Factory/.aws-c-common.new.30101 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "aws-c-common"

Wed May 14 17:01:53 2025 rev:26 rq:1277358 version:0.12.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/aws-c-common/aws-c-common.changes        
2025-04-04 17:34:33.881646700 +0200
+++ /work/SRC/openSUSE:Factory/.aws-c-common.new.30101/aws-c-common.changes     
2025-05-14 17:02:24.462253229 +0200
@@ -1,0 +2,10 @@
+Wed May 14 08:43:51 UTC 2025 - John Paul Adrian Glaubitz 
<adrian.glaub...@suse.com>
+
+- Update to 0.12.3
+  * Make exports consistent by @DmitriyMusatkin in (#1196)
+  * Error handling docs by @graebm in (#1197)
+  * Fix casing on Windows header files by @waahm7 in (#1199)
+  * Add reset src api for aws_cbor_decoder by @TingDaoK in (#1202)
+  * Fix invalid XML Buffer Overflow Error by @waahm7 in (#1201)
+
+-------------------------------------------------------------------

Old:
----
  v0.12.2.tar.gz

New:
----
  v0.12.3.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ aws-c-common.spec ++++++
--- /var/tmp/diff_new_pack.LNqOja/_old  2025-05-14 17:02:26.050319918 +0200
+++ /var/tmp/diff_new_pack.LNqOja/_new  2025-05-14 17:02:26.058320254 +0200
@@ -19,7 +19,7 @@
 %define library_version 1.0.0
 %define library_soversion 1
 Name:           aws-c-common
-Version:        0.12.2
+Version:        0.12.3
 Release:        0
 Summary:        Core C99 package for AWS SDK for C
 License:        Apache-2.0

++++++ v0.12.2.tar.gz -> v0.12.3.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/aws-c-common-0.12.2/AWSCRTAndroidTestRunner/gradle/wrapper/gradle-wrapper.properties
 
new/aws-c-common-0.12.3/AWSCRTAndroidTestRunner/gradle/wrapper/gradle-wrapper.properties
--- 
old/aws-c-common-0.12.2/AWSCRTAndroidTestRunner/gradle/wrapper/gradle-wrapper.properties
    2025-03-26 19:14:51.000000000 +0100
+++ 
new/aws-c-common-0.12.3/AWSCRTAndroidTestRunner/gradle/wrapper/gradle-wrapper.properties
    2025-05-12 18:06:01.000000000 +0200
@@ -1,6 +1,6 @@
-#Wed Apr 01 11:18:00 PDT 2020
+#Fri May 09 10:40:15 PDT 2025
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-common-0.12.2/README.md 
new/aws-c-common-0.12.3/README.md
--- old/aws-c-common-0.12.2/README.md   2025-03-26 19:14:51.000000000 +0100
+++ new/aws-c-common-0.12.3/README.md   2025-05-12 18:06:01.000000000 +0200
@@ -99,6 +99,8 @@
 * For APIs returning an allocated instance of an object, return the memory on 
success, and `NULL` on failure. Before
 returning control to the caller, if you have an error to raise, use the 
`aws_raise_error()` function.
 
+See [error-handling.md](docs/error-handling.md) for a longer tutorial.
+
 #### Log Subjects & Error Codes
 The logging & error handling infrastructure is designed to support multiple 
libraries. For this to work, AWS maintained libraries
 have pre-slotted log subjects & error codes for each library. The currently 
allocated ranges are:
@@ -229,11 +231,17 @@
     AWS_COMMON_API
  void aws_module_destroy(aws_module_t *module);
 
-* Avoid c-strings, and don't write code that depends on `NULL` terminators. 
Expose `struct aws_byte_buf` APIs
-and let the user figure it out.
+* Avoid c-strings, and don't write code that depends on `NULL` terminators.
+    * Pass strings via `struct aws_byte_cursor`. This is a non-owning view 
type. Pass it by value. Strings passed this way do not need a `NULL` terminator.
+    * Only pass `const char *` when thinly wrapping an OS function that 
*requires* a `NULL` terminator.
+    * Store const strings as `struct aws_string *`
+    * Store mutable string buffers as `struct aws_byte_buf`
+
 * There is only one valid character encoding-- UTF-8. Try not to ever need to 
care about character encodings, but
 where you do, the working assumption should always be UTF-8 unless it's 
something we don't get a choice in (e.g. a protocol
 explicitly mandates a character set).
+* If a function has many arguments, or any optional arguments use an 
options-struct.
+* If a variable is time-related, always include the unit of time in its name 
(e.g. `timeout_ms`).
 * If you are adding/using a compiler specific keyword, macro, or intrinsic, 
hide it behind a platform independent macro
 definition. This mainly applies to header files. Obviously, if you are writing 
a file that will only be built on a certain
 platform, you have more liberty on this.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-common-0.12.2/docs/error-handling.md 
new/aws-c-common-0.12.3/docs/error-handling.md
--- old/aws-c-common-0.12.2/docs/error-handling.md      1970-01-01 
01:00:00.000000000 +0100
+++ new/aws-c-common-0.12.3/docs/error-handling.md      2025-05-12 
18:06:01.000000000 +0200
@@ -0,0 +1,453 @@
+# Error Handling
+
+## Basics
+
+Every function that returns an `int` type returns either `AWS_OP_SUCCESS` (0) 
on success, or `AWS_OP_ERR` (-1) on failure.
+
+```c
+/* Example of function returning int type.
+ * Returns AWS_OP_SUCCESS (0) on success.
+ * Returns AWS_OP_ERR (-1) on failure. */
+int aws_do_something(...);
+```
+
+And every function that returns a pointer, returns `NULL` on failure.
+
+```c
+/* Example of function returning a pointer type.
+ * Returns a valid pointer on success.
+ * Returns NULL on failure. */
+struct aws_thing *aws_thing_new(...);
+```
+
+After a function fails, you call `aws_last_error()` to retrieve the actual 
error code. You can get strings for each error code via:
+* `const char *aws_error_name(int error_code)` for the enum name
+  * e.g. `34` -> `"AWS_ERROR_INVALID_ARGUMENT"`
+
+* `const char *aws_error_str(int error_code)` for the English description
+  * e.g. `34` -> `"An invalid argument was passed to a function."`
+
+This error code is stored in a thread-local variable.
+
+If you're authoring a function that needs to fail, you MUST call 
`aws_raise_error(error_code)` to set that thread-local error code before 
returning `AWS_OP_ERR` or `NULL`.
+
+### Design
+
+We do error handling this way because that's how other C libs do it. For 
example, [fopen()](https://en.cppreference.com/w/c/io/fopen) returns a pointer 
and if it's `NULL` you check `errno` to find out why. This way, functions can 
simply return a pointer, without polluting their function signature with 
confusing out-params. For consistency, we had other functions use thread-local 
variables as well, returning `AWS_OP_ERR` (-1) instead of returning the actual 
error code.
+
+While this design pattern makes for graceful looking user code, it is very 
easy to make mistakes in implementation code, if you forget to call 
`aws_raise_error()` in all possible failure branches. Hence, this guide...
+
+### Catching Errors
+
+Error handling in a toy application might look like:
+```c
+/* For functions that return a pointer, NULL means failure */
+struct aws_thing *thing = aws_thing_new(...);
+if (thing == NULL) {
+    int error_code = aws_last_error();
+    printf("Failed to create thing: %s\n", aws_error_str(error_code));
+    exit(error_code);
+}
+
+/* For functions that return an int, AWS_OP_ERR means failure */
+if (aws_thing_do_something_that_may_fail(thing, ...) == AWS_OP_ERR) {
+    int error_code = aws_last_error();
+    printf("Failed to do something: %s\n", aws_error_str(error_code));
+    exit(error_code);
+}
+
+/* You'll also see lots of code that doesn't explicitly compare the return 
value
+ * against `AWS_OP_ERR` or `AWS_OP_SUCCESS`. Since AWS_OP_SUCCESS is zero,
+ * it's "falsey", so the if branch is only taken on error. */
+if (aws_thing_do_something_that_may_fail(thing, ...)) {
+    int error_code = aws_last_error();
+    printf("Failed to do something: %s\n", aws_error_str(error_code));
+    exit(error_code);
+}
+
+/* DO NOT DO THIS */
+aws_thing_do_something_that_may_fail(thing, ...); /* BAD */
+if (aws_last_error() != 0) { /* BAD */
+    /* This code assumes something went wrong because the thread-local
+     * error code is non-zero. BUT nobody ever bothers to clear the
+     * thread-local error code! It could be a stale value from earlier
+     * in the program, and that error might have been handled just fine.
+     * You should only check aws_last_error() after checking that a function
+     * explicitly returned `AWS_OP_ERR` or `NULL` to indicate failure. */
+    printf("I assume previous function call just failed");
+    exit(aws_last_error());
+}
+```
+
+### Raising Errors
+
+When writing functions that may fail, you MUST ensure `aws_raise_error()` is 
called, to set that thread-local error code, before you ultimately return 
`NULL` or `AWS_OP_ERR`. DO NOT accidentally return the error-code itself.
+
+```c
+/* Sample for function that returns pointer type, where NULL means failure */
+struct aws_thing *aws_thing_new(struct aws_allocator *allocator, uint32_t 
num_shinies) {
+    if (num_shinies > AWS_NUM_SHINIES_MAX) {
+        /* Set thread-local error code */
+        aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
+        /* Return NULL to indicate failure */
+        return NULL;
+    }
+
+    struct aws_thing *thing = aws_mem_calloc(allocator, 1, sizeof(struct 
aws_thing));
+    thing->num_shinies = num_shinies;
+
+    /* Returning a valid pointer means success */
+    return thing;
+}
+
+/* Sample for function that returns int, where AWS_OP_ERR(-1) means failure */
+int aws_sub_u64_checked(uint64_t a, uint64_t b, uint64_t *result) {
+    if (a < b) {
+        aws_raise_error(AWS_ERROR_OVERFLOW_DETECTED);
+        return AWS_OP_ERR;
+
+        /* You can also do: return 
aws_raise_error(AWS_ERROR_OVERFLOW_DETECTED);
+         * aws_raise_error() always return AWS_OP_ERR, so you can call it on
+         * the same line as return, if your function returns int. */
+    }
+
+    *result = a - b;
+    return AWS_OP_SUCCESS;
+}
+
+
+/* DO NOT DO THIS */
+int aws_sub_u64_checked(uint64_t a, uint64_t b, uint64_t *result) {
+    if (a < b)
+        /* BAD: Failed to set thread-local error code. */
+        return AWS_OP_ERR;
+    ...
+
+/* DO NOT DO THIS */
+int aws_sub_u64_checked(uint64_t a, uint64_t b, uint64_t *result) {
+    if (a < b)
+        /* BAD: Failed to set thread-local error code.
+         * BAD: Accidentally returned error code, but should return 
AWS_OP_ERR. */
+        return AWS_ERROR_OVERFLOW_DETECTED;
+    ...
+```
+
+If you're writing a function that may fail, and it needs to call other `aws_` 
functions that may fail, you can assume the other function set the error code 
explaining why.
+
+```c
+int aws_my_new_function(...) {
+    if (aws_some_other_thing_that_may_fail(...) != AWS_OP_SUCCESS) {
+        /* Here, it's OK not to call aws_raise_error().
+         * The other aws_ function already set the thread-local error code
+         * that we want the user to see. */
+        return AWS_OP_ERR;
+    }
+    ...
+```
+
+But beware that the thread-local error code can change. You may want to store 
it (e.g. `int error_code = aws_last_error();`) if you need to do a lot of other 
stuff between the error originally occurring, and your function finally 
returning `AWS_OP_ERR`. In general, simple cleanup and logging functions won't 
change the error code. But if you're doing more than that, may as well be safe 
and store it. Here's an example:
+```c
+    ...
+    if (aws_some_other_thing_that_may_fail(...) != AWS_OP_SUCCESS) {
+        /* Preserve error code */
+        int error_code = aws_last_error();
+
+        ...many more lines of code...
+        ...maybe invoking user callbacks, where anything can happen...
+        ...and maybe thread-local error code gets changed...
+
+        /* Re-raise, ensuring caller gets the error code you intend */
+        return aws_raise_error(error_code);
+    }
+```
+
+### Error codes in callbacks
+
+For asynchronous APIs, where results are delivered via callback, error codes 
are delivered by their actual value. A callback function might look like:
+```c
+void on_some_async_operation_complete(int error_code, void *user_data) {
+    if (error_code != 0) {
+        printf("Operation failed: %s\n", aws_error_str(error_code));
+        ...
+```
+
+ The thread-local error code variable is not used with callbacks. `AWS_OP_ERR` 
and `AWS_OP_SUCCESS` are not used. DO NOT call `aws_raise_error()` before 
invoking a callback. DO NOT call `aws_last_error()` within a callback to check 
for error.
+
+## Best Practices
+
+Error handling is extra difficult in C, because C doesn't have automatic 
destructors or garbage collection. If you need to do several things in 
sequence, and the last thing fails, you need to manually undo all previous 
steps.
+
+We suggest keeping things as simple and maintainable as we can get away with.
+
+Our style has evolved with time, and you'll find lots of old code violating 
these rules, but here are best practices.
+
+### If a function can't fail, make it void
+
+Don't return `int` "just in case it might fail someday" or "for consistency 
with other functions".
+
+This is especially important for low-level datastructure APIs, or any function 
that cleans up or destroys something.
+
+Imagine there's some "clean up" function that needs to use your function, and 
your function returns `int`. What is that clean up function supposed to do? Try 
to handle the error? How? Should it stop even trying to clean up? Leak memory?
+
+If your function creates some complex stateful class, that takes lots of 
parameters, and can't currently fail, but you can totally imagine a future 
where it can, then yeah give it an API where it might fail. But for simple 
stuff that's just allocating: no.
+
+### Memory Allocation Cannot Fail
+
+Don't write code that tries to handle Out Of Memory (OOM). `aws_mem_acquire()` 
and `aws_mem_calloc()` will never return `NULL`, they will simply abort if the 
OS fails to allocate.
+
+For the first few months of aws-c development, we tried to handle Out Of 
Memory. Lots of code was written that tried to deal with this; little of it was 
ever tested. In the end we decided it wasn't worth the effort. Swift and Rust 
just abort if you push into a vector and it can't expand. We should too. Now, 
`aws_allocator` will abort before it ever returns `NULL`.
+
+Unfortunately, after making this change we didn't go back and simplify APIs 
that could no longer fail, because it would be a large breaking change. So 
functions like `aws_hash_table_put()` return `int` but it's always 
`AWS_OP_SUCCESS` and you should never bother checking it. It sucks, but you 
just have to "know" which errors should or shouldn't be checked for. It's 
generally just our older datastructure APIs that suffer from this (e.g. 
aws_byte_buf, aws_array_list, aws_hash_table). If you initialized these with an 
allocator, so they can grow dynamically, then they can't fail. But if you 
initialized them with a fixed size, or no allocator, you should still check for 
errors. For pretty much all other APIs, you must still check for errors.
+
+Also, we didn't proactively remove error-handling code where the errors 
couldn't happen anymore. But if you're ever editing such code, do please 
simplify it along with your changes! Also, if you find an old function that 
can't fail, but its docs say it may, update the docs!
+
+### Don't try to handle NULL (except in destructors)
+
+If a function doesn't expect `NULL`, then don't try to handle `NULL`.
+
+Feel free to add `AWS_ASSERT(some_pointer);` if you want your code to be clear 
about its refusal to deal with NULL.
+
+Imagine you tried to handle `NULL`, then functions then function that could be 
`void` now must return `int`, just in case `NULL` is passed in and they can't 
do anything! Now you have a function with an error code, but it can't possible 
fail in working code. But users wonder whether or not they should be checking 
for failure from this function... and maybe stop checking for errors on other 
functions that *can* fail randomly... if it's a mess. Also, be honest, you're 
never going to add tests for all that error handling code.
+
+One big exception is destructors (i.e. `clean_up()`, `destroy()`, 
`release()`). We'll touch on this later, but to make error-handling code as 
simple as possible, we want it to be safe to call destructor functions whether 
or not the thing was ever successfully created.
+
+### AWS_ASSERT() vs AWS_FATAL_ASSERT() vs aws_raise_error()
+
+How do you choose when to assert, vs real error handling? Asserts will simply 
abort the program, so only use them for detecting internal bugs. We *never* 
want users from Java or Python to hit an assert. So things like validating a 
number's value should be real error checks, because the number might come from 
a Java user.
+
+`AWS_ASSERT()` does nothing in Release builds, it only checks in Debug. Use 
this for basic sanity checks that NULL isn't being passed into a function, or 
as a comment to future programmers "don't worry, this can't possibly be true 
right now", or to do an expensive check you don't want happening in Release 
builds. Note that most language bindings (e.g. _awscrt.so in aws-crt-python) 
are *never* built in Debug.
+
+`AWS_FATAL_ASSERT()` still runs in Release builds. Use this to catch bugs that 
might occur in language binding code (which is seldom built in Debug), or for 
internal bugs you worry are too complex to encounter in a Debug build.
+
+Use `aws_raise_error()` (real error handling) for everything else.
+
+### init() / clean_up() example
+
+Here's an example, with an "init / clean_up" style struct, where any 
initialization step may fail, and we'd need to undo the previous steps:
+```c
+int aws_thing_init(struct aws_thing *thing) {
+    /* First, zero things out, in case memory was uninitialized.
+     * That way we can safely call clean_up() on each member,
+     * even if we didn't fully initialize everything.*/
+    AWS_ZERO_STRUCT(*thing);
+
+    if (aws_complex_part_1_init(&thing->part_1) != AWS_OP_SUCCESS) {
+        /* Prefer early-out patterns, vs deeply embedded if-branches */
+        goto error;
+    }
+
+    if (aws_complex_part_2_init(&thing->part_2) != AWS_OP_SUCCESS) {
+        /* goto is fine in C */
+        goto error;
+    }
+
+    if (aws_complex_part_3_init(&thing->part_3) != AWS_OP_SUCCESS) {
+        goto error;
+    }
+
+    return AWS_OP_SUCCESS;
+
+error:
+    /* Just call the clean_up() function, rather than repeating
+     * the code that cleans up individual members */
+    aws_thing_clean_up(thing);
+
+    /* Note: we didn't set thread-local error code before returning AWS_OP_ERR,
+     * because we assume it was already set by the aws_complex_part_N_init()
+     * failure that led us here */
+    return AWS_OP_ERR;
+}
+
+void aws_thing_clean_up(struct aws_thing *thing) {
+    /* It's safe to try and clean up aws_ types, even if they were never
+     * initialized. We build all our "destructors" to be idempotent
+     * and safe to call on zeroed-out memory.
+     * This lets us have nice clean simple clean up code like so: */
+    aws_complex_part_3_clean_up(&thing->part_3);
+    aws_complex_part_2_clean_up(&thing->part_2);
+    aws_complex_part_1_clean_up(&thing->part_1);
+
+    /* Finally, zero things out so the clean_up() is idempotent */
+    AWS_ZERO_STRUCT(*thing);
+}
+```
+
+And here are some ways NOT to do it:
+```c
+/* DO NOT DO THIS (each failed step tries to clean up previous steps) */
+int aws_thing_init(struct aws_thing *thing) {
+    AWS_ZERO_STRUCT(*thing);
+
+    if (aws_complex_part_1_init(&thing->part_1) != AWS_OP_SUCCESS) {
+        return AWS_OP_ERR;
+    }
+
+    if (aws_complex_part_2_init(&thing->part_2) != AWS_OP_SUCCESS) {
+        aws_complex_part_1_clean_up(&thing->part_1);
+        return AWS_OP_ERR;
+    }
+
+    if (aws_complex_part_3_init(&thing->part_3) != AWS_OP_SUCCESS) {
+        /* BAD: If clean up code is in so many places, and subtly different
+         * each time, it's really easy to make a mistake. */
+        aws_complex_part_2_clean_up(&thing->part_2);
+        aws_complex_part_1_clean_up(&thing->part_1);
+        return AWS_OP_ERR;
+    }
+
+    return AWS_OP_SUCCESS;
+}
+
+/* DO NOT DO THIS (multiple goto labels) */
+int aws_thing_init(struct aws_thing *thing) {
+    AWS_ZERO_STRUCT(*thing);
+
+    if (aws_complex_part_1_init(&thing->part_1) != AWS_OP_SUCCESS) {
+        return AWS_OP_ERR;
+    }
+
+    if (aws_complex_part_2_init(&thing->part_2) != AWS_OP_SUCCESS) {
+        goto clean_up_part_1;
+    }
+
+    if (aws_complex_part_3_init(&thing->part_3) != AWS_OP_SUCCESS) {
+        /* BAD: having multiple goto labels is a copy/paste bug waiting to 
happen */
+        goto clean_up_part_2;
+    }
+
+    return AWS_OP_SUCCESS;
+
+clean_up_part_2:
+    /* BAD: if you try to clean up individual parts, you repeat code that's
+     * already in clean_up(). You risk bugs where someone adds a new member,
+     * and forgets to clean it up from here too. */
+    aws_complex_part_2_clean_up(&thing->part_2);
+clean_up_part_1:
+    aws_complex_part_1_clean_up(&thing->part_1);
+    return AWS_OP_ERR;
+}
+
+/* DO NOT DO THIS (deep if statements) */
+int aws_thing_init(struct aws_thing *thing) {
+    AWS_ZERO_STRUCT(*thing);
+
+    if (aws_complex_part_1_init(&thing->part_1) == AWS_OP_SUCCESS) {
+        if (aws_complex_part_2_init(&thing->part_2) == AWS_OP_SUCCESS) {
+            if (aws_complex_part_3_init(&thing->part_3) == AWS_OP_SUCCESS) {
+                /* BAD: You end up with most of your code indented
+                 * 5+ levels deep. Adding more checks results in a
+                 * huge diff as everything get indented even more.
+                 * It actually doesn't look so bad in this example,
+                 * but believe me it sucks in real life. */
+                return AWS_OP_SUCCESS;
+            }
+            aws_complex_part_2_clean_up(&thing->part_2);
+        }
+        aws_complex_part_1_clean_up(&thing->part_1);
+    }
+
+    return AWS_OP_ERR;
+}
+```
+
+### new() / destroy() example
+
+Here's an example that returns a pointer, and does a bit more:
+```c
+struct aws_thing *aws_thing_new(struct aws_allocator *alloc,
+                                    const struct aws_thing_options *options) {
+    /* DO NOT do real error-checking for NULL args, that way lies madness.
+     * You MAY do these debug-only statements that kill the program */
+    AWS_ASSERT(alloc);
+    AWS_ASSERT(options);
+
+    /* Before creating anything, do real error checking on values that
+     * might come down from language bindings. Not NULL checks, but other
+     * more subtle stuff */
+    if (options->timeout_ms > AWS_THING_TIMEOUT_MS_MAX) {
+        /* Log the exact issue, since "invalid argument" is vague
+         * when there are lots of arguments */
+        AWS_LOGF_ERROR(..., "timeout_ms has invalid value");
+        /* Set thread-local error code before returning NULL */
+        aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
+        return NULL;
+    }
+
+    /* Use aws_mem_calloc() to get memory that's already zeroed-out. This
+     * reduces bugs where you forget to initialize a member */
+    struct aws_thing *thing = aws_mem_calloc(alloc, 1, sizeof(struct 
aws_thing));
+
+    /* Don't need to check if thing == NULL because allocation can't fail */
+
+    /* Set allocator (and other things that can't possibly fail) first, so we
+     * can use the destroy() function to clean up if anything else goes wrong 
*/
+    thing->alloc = alloc;
+    thing->timeout_ms = options->timeout_ms;
+
+    if (aws_complex_part_1_init(&thing->part_1) != AWS_OP_SUCCESS) {
+        goto error;
+    }
+
+    if (aws_complex_part_2_init(&thing->part_2) != AWS_OP_SUCCESS) {
+        goto error;
+    }
+
+    if (aws_complex_part_3_init(&thing->part_3) != AWS_OP_SUCCESS) {
+        goto error;
+    }
+
+    return thing;
+
+error:
+    aws_thing_destroy(thing);
+
+    /* Note: we didn't set thread-local error code before returning NULL,
+     * because we assume it was already set by the aws_complex_part_N_init()
+     * failure that led us here */
+    return NULL;
+}
+
+void aws_thing_destroy(struct aws_thing *thing) {
+    /* "destructor" functions are the only place we check for NULL, so user
+     * can naively call it, whether or not it was successfully created */
+    if (thing == NULL) {
+        return;
+    }
+
+    aws_complex_part_3_clean_up(&thing->part_3);
+    aws_complex_part_2_clean_up(&thing->part_2);
+    aws_complex_part_1_clean_up(&thing->part_1);
+
+    aws_mem_release(thing->alloc, thing);
+}
+```
+
+## In Hindsight
+
+It's too late to change any of this, but here's graebm's personal opinion for 
anyone starting down a similar path.
+
+### thread-local error codes are too hard
+
+IMHO it's too easy to make a mistake, and forget to `aws_raise_error()` before 
returning `AWS_OP_ERR`. When this happens, the user gets a nonsense value from 
`aws_last_error()`, and it's very hard to know what is actually failing.
+
+IMHO we should have done something like Rust's [std::result<T, 
E>](https://doc.rust-lang.org/std/result/), Swift's 
[Result<Success,Failure>](https://developer.apple.com/documentation/swift/result),
 and C++'s 
[std::expected<T,E>](https://en.cppreference.com/w/cpp/utility/expected). If we 
returned errors by value, we wouldn't have bugs where we forgot to set 
`aws_raise_error()`, or bugs where `aws_last_error()` was accidentally 
overwritten. C doesn't have generics, but you can create something like them 
using macros (see 
[future.h](https://github.com/awslabs/aws-c-io/blob/7e75f17400ca2cfdc296865b9c2b10323f49a9c5/include/aws/io/future.h#L30)),
 or just write:
+```c
+struct aws_thing_result {
+    struct aws_thing *ok;
+    int err;
+};
+
+struct aws_thing_result aws_thing_new(...);
+```
+
+And functions that don't return pointers can just return the error code by 
value:
+```
+/* returns AWS_OK (0) on success, or the error code */
+int aws_thing_do_something_that_may_fail(...);
+```
+
+### int error codes lose information
+
+Exceptions in other languages can carry a lot more information than int error 
codes. If a Java exception occurs within a C callback, all the information from 
that callback must be thrown out. The Java user ultimately sees something 
less-than-useful like AWS_ERROR_HTTP_CALLBACK_FAILURE. We must tell the Java 
user to enable CRT logging to see what's actually going wrong. It's not 
idiomatic.
+
+IMHO errors shouldn't just be ints, they should carry lots more information 
(e.g. custom message, chain of caused_by, is_retryable, etc). But this would 
make them "heavier", so we wouldn't want to use them somewhere performance 
sensitive, like querying a hash table. We could reserve "rich" errors for the 
APIs that language bindings call directly. We might even be able to create a 
Java exception chain where some of those came from our "rich" errors.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-common-0.12.2/include/aws/common/cbor.h 
new/aws-c-common-0.12.3/include/aws/common/cbor.h
--- old/aws-c-common-0.12.2/include/aws/common/cbor.h   2025-03-26 
19:14:51.000000000 +0100
+++ new/aws-c-common-0.12.3/include/aws/common/cbor.h   2025-05-12 
18:06:01.000000000 +0200
@@ -313,6 +313,17 @@
 size_t aws_cbor_decoder_get_remaining_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.
+ *
+ * @param decoder
+ * @param src   The src data to decode from..
+ */
+AWS_COMMON_API
+void aws_cbor_decoder_reset_src(struct aws_cbor_decoder *decoder, struct 
aws_byte_cursor src);
+
+/**
  * @brief Decode the next element and store it in the decoder cache if there 
was no element cached.
  * If there was element cached, just return the type of the cached element.
  *
@@ -414,9 +425,9 @@
 int aws_cbor_decoder_pop_next_array_start(struct aws_cbor_decoder *decoder, 
uint64_t *out_size);
 
 /**
- * @brief Get the next AWS_CBOR_TYPE_MAP_START element. Only consume the 
AWS_CBOR_TYPE_MAP_START element and set the
- * size of array to *out_size, not the content of the map. The next *out_size 
pair of cbor data items as key and value
- * will be the content of the array for a valid cbor data,
+ * @brief Get the next AWS_CBOR_TYPE_MAP_START element.
+ * Only consume the AWS_CBOR_TYPE_MAP_START element and set the size of array 
to *out_size, not the content of the map.
+ * The next *out_size pair of cbor data items as key and value will be the 
content of the array for a valid cbor data,
  *
  * Notes: For indefinite-length, this function will fail with 
"AWS_ERROR_CBOR_UNEXPECTED_TYPE". The designed way to
  * handle indefinite-length is:
@@ -432,12 +443,12 @@
 int aws_cbor_decoder_pop_next_map_start(struct aws_cbor_decoder *decoder, 
uint64_t *out_size);
 
 /**
- * @brief Get the next AWS_CBOR_TYPE_TAG element. Only consume the 
AWS_CBOR_TYPE_TAG element and set the
- * tag value to *out_tag_val, not the content of the tagged. The next cbor 
data item will be the content of the tagged
- * value for a valid cbor data.
+ * @brief Get the next AWS_CBOR_TYPE_TAG element.
+ * Only consume the AWS_CBOR_TYPE_TAG element and set the tag ID value to 
*out_tag_val, not the content of the tagged.
+ * The next cbor data item will be the content of the tagged value for a valid 
cbor data.
  *
  * @param decoder
- * @param out_size store the size of map if succeed.
+ * @param out_tag_val store the value of tag ID.
  * @return AWS_OP_SUCCESS successfully consumed the next element and get the 
result, otherwise AWS_OP_ERR.
  */
 AWS_COMMON_API
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-common-0.12.2/include/aws/common/exports.h 
new/aws-c-common-0.12.3/include/aws/common/exports.h
--- old/aws-c-common-0.12.2/include/aws/common/exports.h        2025-03-26 
19:14:51.000000000 +0100
+++ new/aws-c-common-0.12.3/include/aws/common/exports.h        2025-05-12 
18:06:01.000000000 +0200
@@ -4,7 +4,7 @@
  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  * SPDX-License-Identifier: Apache-2.0.
  */
-#if defined(AWS_C_RT_USE_WINDOWS_DLL_SEMANTICS) || defined(_WIN32)
+#if defined(AWS_CRT_USE_WINDOWS_DLL_SEMANTICS) || defined(_WIN32)
 #    ifdef AWS_COMMON_USE_IMPORT_EXPORT
 #        ifdef AWS_COMMON_EXPORTS
 #            define AWS_COMMON_API __declspec(dllexport)
@@ -15,15 +15,15 @@
 #        define AWS_COMMON_API
 #    endif /* AWS_COMMON_USE_IMPORT_EXPORT */
 
-#else /* defined (AWS_C_RT_USE_WINDOWS_DLL_SEMANTICS) || defined (_WIN32) */
+#else /* defined (AWS_CRT_USE_WINDOWS_DLL_SEMANTICS) || defined (_WIN32) */
 
-#    if ((__GNUC__ >= 4) || defined(__clang__)) && 
defined(AWS_COMMON_USE_IMPORT_EXPORT) && defined(AWS_COMMON_EXPORTS)
+#    if defined(AWS_COMMON_USE_IMPORT_EXPORT) && defined(AWS_COMMON_EXPORTS)
 #        define AWS_COMMON_API __attribute__((visibility("default")))
 #    else
 #        define AWS_COMMON_API
-#    endif /* __GNUC__ >= 4 || defined(__clang__) */
+#    endif
 
-#endif /* defined (AWS_C_RT_USE_WINDOWS_DLL_SEMANTICS) || defined (_WIN32) */
+#endif /* defined (AWS_CRT_USE_WINDOWS_DLL_SEMANTICS) || defined (_WIN32) */
 
 #ifdef AWS_NO_STATIC_IMPL
 #    define AWS_STATIC_IMPL AWS_COMMON_API
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-common-0.12.2/include/aws/common/xml_parser.h 
new/aws-c-common-0.12.3/include/aws/common/xml_parser.h
--- old/aws-c-common-0.12.2/include/aws/common/xml_parser.h     2025-03-26 
19:14:51.000000000 +0100
+++ new/aws-c-common-0.12.3/include/aws/common/xml_parser.h     2025-05-12 
18:06:01.000000000 +0200
@@ -6,6 +6,7 @@
  * SPDX-License-Identifier: Apache-2.0.
  */
 
+/* WARNING: This is not a public API. It is only intended for use within the 
aws-c libraries. */
 #include <aws/common/array_list.h>
 #include <aws/common/byte_buf.h>
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-common-0.12.2/source/arch/arm/windows/cpuid.c 
new/aws-c-common-0.12.3/source/arch/arm/windows/cpuid.c
--- old/aws-c-common-0.12.2/source/arch/arm/windows/cpuid.c     2025-03-26 
19:14:51.000000000 +0100
+++ new/aws-c-common-0.12.3/source/arch/arm/windows/cpuid.c     2025-05-12 
18:06:01.000000000 +0200
@@ -13,8 +13,8 @@
  * permissions and limitations under the License.
  */
 
-#include <Windows.h>
 #include <aws/common/cpuid.h>
+#include <windows.h>
 
 bool aws_cpu_has_feature(enum aws_cpu_feature_name feature_name) {
     switch (feature_name) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-common-0.12.2/source/cbor.c 
new/aws-c-common-0.12.3/source/cbor.c
--- old/aws-c-common-0.12.2/source/cbor.c       2025-03-26 19:14:51.000000000 
+0100
+++ new/aws-c-common-0.12.3/source/cbor.c       2025-05-12 18:06:01.000000000 
+0200
@@ -320,6 +320,13 @@
     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 */
+    decoder->cached_context.type = AWS_CBOR_TYPE_UNKNOWN;
+    decoder->error_code = AWS_ERROR_SUCCESS;
+}
+
 #define LIBCBOR_VALUE_CALLBACK(field, callback_type, cbor_type)                
                                        \
     static void s_##field##_callback(void *ctx, callback_type val) {           
                                        \
         struct aws_cbor_decoder *decoder = ctx;                                
                                        \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-common-0.12.2/source/xml_parser.c 
new/aws-c-common-0.12.3/source/xml_parser.c
--- old/aws-c-common-0.12.2/source/xml_parser.c 2025-03-26 19:14:51.000000000 
+0100
+++ new/aws-c-common-0.12.3/source/xml_parser.c 2025-05-12 18:06:01.000000000 
+0200
@@ -127,6 +127,12 @@
             goto clean_up;
         }
 
+        if (start > location) {
+            AWS_LOGF_ERROR(AWS_LS_COMMON_XML_PARSER, "XML document is 
invalid.");
+            parser.error = aws_raise_error(AWS_ERROR_INVALID_XML);
+            goto clean_up;
+        }
+
         aws_byte_cursor_advance(&parser.doc, start - parser.doc.ptr);
         /* if these are preamble statements, burn them. otherwise don't seek 
at all
          * and assume it's just the doc with no preamble statements. */
@@ -227,6 +233,7 @@
                 }
             }
             size_t skip_len = close_find_result.ptr - parser->doc.ptr;
+
             aws_byte_cursor_advance(&parser->doc, skip_len + 
closing_cmp_buf.len);
             depth_count--;
             break;
@@ -288,7 +295,7 @@
 
         const uint8_t *end_location = memchr(parser->doc.ptr, '>', 
parser->doc.len);
 
-        if (!end_location) {
+        if (!end_location || next_location >= end_location) {
             AWS_LOGF_ERROR(AWS_LS_COMMON_XML_PARSER, "XML document is 
invalid.");
             aws_raise_error(AWS_ERROR_INVALID_XML);
             goto error;
@@ -301,7 +308,6 @@
         }
 
         size_t node_name_len = end_location - next_location;
-
         aws_byte_cursor_advance(&parser->doc, end_location - parser->doc.ptr + 
1);
 
         if (parent_closed) {
@@ -368,13 +374,14 @@
     const uint8_t *next_location = memchr(parser->doc.ptr, '<', 
parser->doc.len);
 
     if (!next_location) {
-        return parser->error;
+        AWS_LOGF_ERROR(AWS_LS_COMMON_XML_PARSER, "XML document is invalid.");
+        return aws_raise_error(AWS_ERROR_INVALID_XML);
     }
 
     aws_byte_cursor_advance(&parser->doc, next_location - parser->doc.ptr);
     const uint8_t *end_location = memchr(parser->doc.ptr, '>', 
parser->doc.len);
 
-    if (!end_location) {
+    if (!end_location || next_location >= end_location) {
         AWS_LOGF_ERROR(AWS_LS_COMMON_XML_PARSER, "XML document is invalid.");
         return aws_raise_error(AWS_ERROR_INVALID_XML);
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-common-0.12.2/tests/CMakeLists.txt 
new/aws-c-common-0.12.3/tests/CMakeLists.txt
--- old/aws-c-common-0.12.2/tests/CMakeLists.txt        2025-03-26 
19:14:51.000000000 +0100
+++ new/aws-c-common-0.12.3/tests/CMakeLists.txt        2025-05-12 
18:06:01.000000000 +0200
@@ -460,7 +460,8 @@
 
 add_test_case(cpuid_test)
 
-add_test_case(xml_parser_root_with_text)
+add_test_case(xml_parser_root_with_text) 
+add_test_case(xml_parser_malformed_end_node_character_before_start_test)
 add_test_case(xml_parser_child_with_text)
 add_test_case(xml_parser_siblings_with_text)
 add_test_case(xml_parser_preamble_and_attributes)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-common-0.12.2/tests/cbor_test.c 
new/aws-c-common-0.12.3/tests/cbor_test.c
--- old/aws-c-common-0.12.2/tests/cbor_test.c   2025-03-26 19:14:51.000000000 
+0100
+++ new/aws-c-common-0.12.3/tests/cbor_test.c   2025-05-12 18:06:01.000000000 
+0200
@@ -438,21 +438,19 @@
     struct aws_cbor_decoder *decoder = aws_cbor_decoder_new(allocator, 
invalid_cbor);
     ASSERT_FAILS(aws_cbor_decoder_peek_type(decoder, &out_type));
     ASSERT_UINT_EQUALS(AWS_ERROR_INVALID_CBOR, aws_last_error());
-    aws_cbor_decoder_destroy(decoder);
 
     /* 2. Empty cursor */
     struct aws_byte_cursor empty = {0};
-    decoder = aws_cbor_decoder_new(allocator, empty);
+    aws_cbor_decoder_reset_src(decoder, empty);
     ASSERT_FAILS(aws_cbor_decoder_peek_type(decoder, &out_type));
     ASSERT_UINT_EQUALS(AWS_ERROR_INVALID_CBOR, aws_last_error());
-    aws_cbor_decoder_destroy(decoder);
 
     /* 3. Try get wrong type */
     struct aws_cbor_encoder *encoder = aws_cbor_encoder_new(allocator);
     uint64_t val = 1;
     aws_cbor_encoder_write_uint(encoder, val);
     struct aws_byte_cursor final_cursor = 
aws_cbor_encoder_get_encoded_data(encoder);
-    decoder = aws_cbor_decoder_new(allocator, final_cursor);
+    aws_cbor_decoder_reset_src(decoder, final_cursor);
     uint64_t out = 0;
     ASSERT_FAILS(aws_cbor_decoder_pop_next_array_start(decoder, &out));
     ASSERT_UINT_EQUALS(AWS_ERROR_CBOR_UNEXPECTED_TYPE, aws_last_error());
@@ -463,7 +461,6 @@
     ASSERT_FAILS(aws_cbor_decoder_consume_next_whole_data_item(decoder));
     ASSERT_FAILS(aws_cbor_decoder_peek_type(decoder, &out_type));
     ASSERT_UINT_EQUALS(AWS_ERROR_INVALID_CBOR, aws_last_error());
-    aws_cbor_decoder_destroy(decoder);
 
     /* 4. Consume data items with size */
     struct aws_byte_cursor val_1 = aws_byte_cursor_from_c_str("my test");
@@ -476,13 +473,13 @@
     aws_cbor_encoder_write_tag(encoder, AWS_CBOR_TAG_NEGATIVE_BIGNUM);
     aws_cbor_encoder_write_bytes(encoder, val_1);
     final_cursor = aws_cbor_encoder_get_encoded_data(encoder);
-    decoder = aws_cbor_decoder_new(allocator, final_cursor);
+    aws_cbor_decoder_reset_src(decoder, final_cursor);
     ASSERT_SUCCESS(aws_cbor_decoder_peek_type(decoder, &out_type));
     ASSERT_UINT_EQUALS(AWS_CBOR_TYPE_MAP_START, out_type);
     ASSERT_SUCCESS(aws_cbor_decoder_consume_next_whole_data_item(decoder));
     ASSERT_UINT_EQUALS(0, aws_cbor_decoder_get_remaining_length(decoder));
-    aws_cbor_decoder_destroy(decoder);
 
+    aws_cbor_decoder_destroy(decoder);
     aws_cbor_encoder_destroy(encoder);
     aws_common_library_clean_up();
     return AWS_OP_SUCCESS;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/aws-c-common-0.12.2/tests/xml_parser_test.c 
new/aws-c-common-0.12.3/tests/xml_parser_test.c
--- old/aws-c-common-0.12.2/tests/xml_parser_test.c     2025-03-26 
19:14:51.000000000 +0100
+++ new/aws-c-common-0.12.3/tests/xml_parser_test.c     2025-05-12 
18:06:01.000000000 +0200
@@ -95,6 +95,28 @@
 
 AWS_TEST_CASE(xml_parser_child_with_text, s_xml_parser_child_with_text_test)
 
+const char *malformed_end_node_character_before_start =
+    "<?xml version=\"1.0\" 
encoding=\"UTF-8\"?><rootNode>><child1>TestBody</child1></rootNode>";
+
+static int s_xml_parser_malformed_end_node_character_before_start_test(struct 
aws_allocator *allocator, void *ctx) {
+    (void)ctx;
+    struct child_text_capture capture;
+    AWS_ZERO_STRUCT(capture);
+
+    struct aws_xml_parser_options options = {
+        .doc = 
aws_byte_cursor_from_c_str(malformed_end_node_character_before_start),
+        .on_root_encountered = s_root_with_child,
+        .user_data = &capture,
+    };
+    ASSERT_ERROR(AWS_ERROR_INVALID_XML, aws_xml_parse(allocator, &options));
+
+    return AWS_OP_SUCCESS;
+}
+
+AWS_TEST_CASE(
+    xml_parser_malformed_end_node_character_before_start_test,
+    s_xml_parser_malformed_end_node_character_before_start_test)
+
 const char *siblings_with_text =
     "<?xml version=\"1.0\" "
     
"encoding=\"UTF-8\"?><rootNode><child1>TestBody</child1><child2>TestBody2</child2></rootNode>";

Reply via email to