Core c99 package for AWS SDK for C. Includes cross-platform primitives, configuration, data structures, and error handling.
Core c99 package for AWS SDK for C. Includes cross-platform primitives, configuration, data structures, and error handling.
This library is licensed under the Apache 2.0 License.
aws-c-common uses CMake for setting up build environments. This library has no non-kernel dependencies so the build is quite
simple.
For example:
git clone [email protected]:awslabs/aws-c-common.git aws-c-common
mkdir aws-c-common-build
cd aws-c-common-build
cmake ../aws-c-common
make -j 12
make test
sudo make install
Keep in mind that CMake supports multiple build systems, so for each platform you can pass your own build system
as the -G
option. For example:
cmake -GNinja ../aws-c-common
ninja build
ninja test
sudo ninja install
Or on windows,
cmake -G "Visual Studio 14 2015 Win64" ../aws-c-common
msbuild.exe ALL_BUILD.vcproj
Every API has a specific set of styles and conventions. We’ll outline them here. These conventions are followed in every
library in the AWS C SDK ecosystem.
Every function that returns an int
type, returns AWS_OP_SUCCESS
( 0 ) or AWS_OP_ERR
(-1) on failure. To retrieve
the error code, use the function aws_last_error()
. Each error code also has a corresponding error string that can be
accessed via the aws_error_str()
function.
In addition, you can install both a global and a thread local error handler by using the aws_set_global_error_handler_fn()
and aws_set_thread_local_error_handler_fn()
functions.
All error functions are in the include/aws/common/error.h
header file.
Any function that allocates and initializes an object will be suffixed with new
(e.g. aws_myobj_new()
). Similarly, these objects will always
have a corresponding function with a destroy
suffix. The new
functions will return the allocated object
on success and NULL
on failure. To respond to the error, call aws_last_error()
. If several new
or destroy
functions are available, the variants should be named like new_x
or destroy_x
(e.g. aws_myobj_new_copy()
or aws_myobj_destroy_secure()
).
Any function that initializes an existing object will be suffixed with init
(e.g. aws_myobj_init()
. These objects will have a corresponding
clean_up
function if necessary. In these cases, you are responsible for making the decisions for how your object is
allocated. The init
functions return AWS_OP_SUCCESS
( 0 ) or AWS_OP_ERR
(-1) on failure. If several init
or
clean_up
functions are available, they should be named like init_x
or clean_up_x
(e.g. aws_myobj_init_static()
or
aws_myobj_clean_up_secure()
).
If you are contributing to this code-base, first off, THANK YOU!. There are a few things to keep in mind to minimize the
pull request turn around time.
These “guidelines” are followed in every library in the AWS C SDK ecosystem.
aws_allocator
and use that. No malloc()
orfree()
calls should be made directly.AWS_ERROR_OOM
error code upon allocation failures. If it is a new()
functioninit()
function, it should return AWS_OP_ERR
.int
error code. The only acceptable return types are AWS_OP_SUCCESS
and AWS_OP_ERR
. Beforeaws_raise_error()
function.NULL
on failure. Beforeaws_raise_error()
function.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:
Range | Library Name |
---|---|
[0x0000, 0x0400) | aws-c-common |
[0x0400, 0x0800) | aws-c-io |
[0x0800, 0x0C00) | aws-c-http |
[0x0C00, 0x1000) | aws-c-compression |
[0x1000, 0x1400) | aws-c-eventstream |
[0x1400, 0x1800) | aws-c-mqtt |
[0x1800, 0x1C00) | aws-c-auth |
[0x1C00, 0x2000) | aws-c-cal |
[0x2000, 0x2400) | aws-crt-cpp |
[0x2400, 0x2800) | aws-crt-java |
[0x2800, 0x2C00) | aws-crt-python |
[0x2C00, 0x3000) | aws-crt-nodejs |
[0x3000, 0x3400) | aws-crt-dotnet |
[0x3400, 0x3800) | aws-c-iot |
[0x3800, 0x3C00) | aws-c-s3 |
[0x3C00, 0x4000) | aws-c-sdkutils |
[0x4000, 0x4400) | aws-crt-kotlin |
[0x4400, 0x4800) | aws-crt-swift |
[0x4800, 0x4C00) | (reserved for future project) |
Each library should begin its error and log subject values at the beginning of its range and follow in sequence (don’t skip codes). Upon
adding an AWS maintained library, a new enum range must be approved and added to the above table.
We have a high bar for test coverage, and PRs fixing bugs or introducing new functionality need to have tests before
they will be accepted. A couple of tips:
We provide a test harness for writing unit tests. This includes an allocator that will fail your test if you have any
memory leaks, as well as some ASSERT
macros. To write a test:
int test_case_name(struct aws_allocator *, void *ctx)
AWS_TEST_CASE
macro to declare the test.tests/main.c
file.tests/CMakeLists.txt
file.(
.else
and else if
stay on the same line as the closing brace.Example:
if (condition) {
do_something();
} else {
do_something_else();
}
aws/common/common.h
, so feel free to use them.//
in header files and prefer block style (/* */
) for long blocks of text. C++ style comments are fine in C files.void *impl
pattern. v-tablesExample:
#ifdef FOO
do_something();
# ifdef BAR
do_something_else();
# endif
#endif
For all error code names with the exception of aws-c-common, use AWS_ERROR_<lib name>_<error name>
.
All error strings should be written using correct English grammar.
SNAKE_UPPER_CASE constants, macros, and enum members.
snake_lower_case everything else.
static
(local file scope) variables that are not const
are prefixed by s_
and lower snake case.
Global variables not prefixed as const
are prefixed by g_
and lower snake case.
Thread local variables are prefixed as tl_
and lower snake case.
Macros and const
variables are upper snake case.
For constants, prefer anonymous enums.
Don’t typedef structs. It breaks forward declaration ability.
Don’t typedef enums. It breaks forward declaration ability.
typedef function definitions for use as function pointers as values and suffixed with _fn.
Do this:
typedef int(fn_name_fn)(void *);
Not this:
typedef int(*fn_name_fn)(void *);
If a callback may be async, then always have it be async.
Callbacks that are sometimes async and sometimes sync are hard to code around and lead to bugs
(see this blog post).
Unfortunately many callbacks in this codebase currently violate this rule,
so be careful. But do not add any more.
Every source and header file must have a copyright header (The standard AWS one for apache 2).
Use standard include guards (e.g. #IFNDEF HEADER_NAME #define HEADER_NAME etc…).
Include order should be:
the header for the translation unit for the .c file
newline
header files in a directory in alphabetical order
newline
header files not in a directory (system and stdlib headers)
Platform specifics should be handled in c files and partitioned by directory.
Do not use extern inline
. It’s too unpredictable between compiler versions and language standards.
Namespace all definitions in header files with aws_<libname>?_<api>_<what it does>
. Lib name is
not always required if a conflict is not likely and it provides better ergonomics.
init
, clean_up
, new
, destroy
are suffixed to the function names for their object.
Example:
AWS_COMMON_API
int aws_module_init(aws_module_t *module);
AWS_COMMON_API
void aws_module_clean_up(aws_module_t *module);
AWS_COMMON_API
aws_module_t *aws_module_new(aws_allocator_t *allocator);
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.
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 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.
When checking more than one error condition, check and log each condition separately with a unique message.
Do this:
if (options->callback == NULL) {
AWS_LOGF_ERROR(AWS_LS_SOME_SUBJECT, "Invalid options - callback is null");
return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
}
if (options->allocator == NULL) {
AWS_LOGF_ERROR(AWS_LS_SOME_SUBJECT, "Invalid options - allocator is null");
return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
}
Not this:
if (options->callback == NULL || options->allocator == NULL) {
AWS_LOGF_ERROR(AWS_LS_SOME_SUBJECT, "Invalid options - something is null");
return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
}
To learn more about CBMC and proofs specifically, review the training material here.
The verification/cbmc/proofs
directory contains CBMC proofs.
In order to run these proofs you will need to install CBMC and other tools by following the instructions here.