Skip to content

About

UTF-8 replacement for C strings, supporting zero-copy use-cases (non-null-terminated).

Resources

Stars

1 star

Watchers

1 watching

Forks

Repository files navigation

c-str-span

License Doc Coverage Test Coverage CI C89

UTF-8 replacement for C strings, supporting zero-copy use-cases (non-null-terminated).

Extracted from https://github.com/Azure/azure-sdk-for-c @ ac28b8 with:

  • No dependency on other Azure SDK for C functionalities;
  • C89 (ANSI C) compliance;
  • CTest integration;
  • More const correctness;
  • Use size_t over int32_t for size types;
  • Implement az_span_printf;
  • Added support for MSVC 2005, Open Watcom (incl. DOS target), MinGW, and Cygwin
  • Header-only amalgamation support (STB-style).

Amalgamation (Header-Only)

This library supports an STB-style header-only distribution via a CMake amalgamation script. To generate the amalgamated header:

cmake -B build -DC_STR_SPAN_BUILD_AMALGAMATION=ON
cmake --build build --target c_str_span_amalgamation

This generates c_str_span_amalgamation.h. To consume it, simply define C_STR_SPAN_IMPLEMENTATION in exactly one source file before including it:

#define C_STR_SPAN_IMPLEMENTATION
#include "c_str_span_amalgamation.h"

int main(void) {
    az_span span = AZ_SPAN_FROM_STR("Hello");
    return (int)az_span_size(span);
}

Documentation originally from: https://github.com/Azure/azure-sdk-for-c/tree/main/sdk/docs/core#working-with-spans, now below:

Working with Spans

An az_span is a small data structure (defined in the c_str_span.h file) wrapping a byte buffer. Specifically, an az_span instance contains:

  • a byte pointer
  • an integer size

Use this to create SDKs that pass az_span instances to functions to ensure that a buffer's address and size are always passed together; this reduces the chance of bugs. And, since we have the size, operations are fast; for example, we never need to call strlen to find the length of a string in order to append to it. Furthermore, when your SDK functions write or copy to an az_span, those functions ensure that we never write beyond the size of the buffer; this prevents data corruption. And finally, when reading from an az_span, we never read past the az_span's size ensuring that we don't process uninitialized data.

Since your SDK functions require az_span parameters, customers must know how to create az_span instances so that you can call functions in their SDK. Here are some examples.

Create an empty az_span:

az_span empty_span = az_span_empty(); /* size = 0 */

Create an az_span expression from a byte buffer:

uint8_t buffer[1024];
some_function(AZ_SPAN_FROM_BUFFER(buffer));  /* size = 1024 */

Create an az_span expression from a string (the span does NOT include the 0-terminating byte):

some_function(AZ_SPAN_FROM_STR("Hello"));  /* size = 5 */

As shown above, an az_span over a string does not include the 0-terminator. If you need to 0-terminate the string, you can call this function to append a 0 byte (if the span's size is large enough to hold the extra byte):

enum az_result_core az_span_copy_u8(az_span destination, uint8_t byte, az_span* out_span);

For example, checking the return value to ensure it didn't fail:

#include <assert.h>
#include <stdio.h>

void append_null_example(void) {
    uint8_t buffer[6];
    az_span destination = AZ_SPAN_FROM_BUFFER(buffer);
    az_span out_span;
    enum az_result_core rc;

    /* Copy "Hello" into the buffer */
    rc = az_span_copy(destination, AZ_SPAN_FROM_STR("Hello"), &out_span);
    if (rc != AZ_OK) {
        printf("Failed to copy span, error code: %d\n", rc);
        assert(rc == AZ_OK);
    }

    /* We use out_span to append the null terminator since out_span represents the remainder */
    rc = az_span_copy_u8(out_span, '\0', &out_span);
    if (rc != AZ_OK) {
        printf("Failed to append null terminator, error code: %d\n", rc);
        assert(rc == AZ_OK);
    }

    /* str points to a 0-terminated string */
    {
        char* str = (char*) az_span_ptr(destination);
        printf("%s\n", str);
    }
}

Or, you can call this function to copy the string in the az_span to your own char* buffer; this function will 0-terminate the string in the char* buffer:

enum az_result_core az_span_to_str(char* destination, size_t destination_max_size, az_span source);

Example handling the return code:

#include <assert.h>
#include <stdio.h>

void to_str_example(void) {
    char destination[16];
    az_span source = AZ_SPAN_FROM_STR("Hello");
    enum az_result_core rc;

    rc = az_span_to_str(destination, sizeof(destination), source);
    if (rc != AZ_OK) {
        printf("Failed to convert span to string, error code: %d\n", rc);
        assert(rc == AZ_OK);
    }
}

There are many functions to manipulate az_span instances. You can slice (subset an az_span), parse an az_span containing a string into a number, format a number as a string into an az_span, check if two az_span instances are equal or the contents of two az_span instances are equal, and more.

Strings

A string is a span of UTF-8 characters. It's not a zero-terminated string. Defined in c_str_span.h.

az_span hello_world = AZ_SPAN_FROM_STR("Hello world!");

License

MIT

About

UTF-8 replacement for C strings, supporting zero-copy use-cases (non-null-terminated).

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages