Skip to content
synesissoftwarePublic

Latest commit

 

History

17 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cstring

Small standalone C library that provides extensible C-style strings and extensible arrays of those strings, for Unix and Windows.

C License GitHub release Last Commit CI

Table of Contents

Introduction

cstring provides one resizeable string, cstring_t, and a vector of those strings, cstring_vector_t. A cstring_t is always a length, a pointer, a capacity, and flags. The flags select a memory contract. An owned growable string is the default. Fixed, borrowed, auto-buffer, and readonly are the other contracts. Which heap owns the memory is a separate choice: realloc by default, and the Windows heaps where those flags exist.

The C API has no non-standard dependencies. Building tests requires STLSoft and xTests (and may optionally recognise shwild).

Usage modes

Ordinary code uses the default contract and never sets a flag. The other contracts exist so a caller can cap growth, write into a buffer they already have, or select a Windows heap, without a second string type.

Default use

#include <cstring/cstring.h>

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    cstring_t  cs;
    CSTRING_RC rc = cstring_create(&cs, "Hello");

    if (CSTRING_RC_SUCCESS != rc)
    {
        return EXIT_FAILURE;
    }

    printf("%s\n", cs.ptr);

    cstring_destroy(&cs);

    return EXIT_SUCCESS;
}

Storage contract

Pass memory flags to cstring_createEx() or cstring_createLenEx(). For a borrowed buffer, arena is that buffer and capacity is its size. With no memory flags, those parameters are ignored and the string is an owned heap allocation.

Mode Flags Familiar form If it cannot grow
Owned, growable (none) A heap std::string, or Rust String CSTRING_RC_OUTOFMEMORY
Owned, fixed CSTRING_F_MEMORY_IS_FIXED That same owned string, with a hard ceiling CSTRING_RC_EXCEEDFIXEDCAPACITY
Borrowed CSTRING_F_MEMORY_IS_BORROWED (implies fixed) A caller-owned char buf[N], writable up to N CSTRING_RC_EXCEEDBORROWEDCAPACITY
Auto-buffer CSTRING_F_MEMORY_IS_BORROWED | CSTRING_F_MEMORY_CAN_GROW_TO_HEAP stlsoft::auto_buffer / llvm::SmallString Spills to the heap, then stays there
Readonly CSTRING_F_MEMORY_IS_READONLY A frozen instance; with borrowed, std::string_view or Rust &str CSTRING_RC_READONLY

std::string SSO keeps a small buffer inside the object; cstring's auto-buffer uses a buffer you supply, of a size you choose, and cstring_t stays four fields; after a spill the instance stays on the heap (Rust's standard String has no SSO).

Allocators

The arena flags apply to memory the library owns: the default heap, a fixed owned buffer, and the heap side of an auto-buffer.

Arena Flag Where
realloc CSTRING_F_USE_REALLOC (the default) Unix and Windows
Win32 global memory CSTRING_F_USE_WINDOWS_GLOBAL_MEMORY Windows
Process heap CSTRING_F_USE_WINDOWS_PROCESSHEAP_MEMORY Windows
COM task allocator CSTRING_F_USE_WINDOWS_COM_TASK_MEMORY Windows

A few further rules:

  • cstring_init() stores cstring_t_DEFAULT. That instance does not need cstring_destroy(). Every cstring_create* does;
  • cstring_yield2() hands back an owned payload. Borrowed and readonly instances refuse it. A Windows DLL built on realloc returns CSTRING_RC_CANNOTYIELDFROMSO;
  • The character type is char unless CSTRING_USE_WIDE_STRINGS is set (normally both UNICODE and _UNICODE on Windows). CSTRING_NO_USE_WIDE_STRINGS forces char. That choice is made at compile time. prepare_cmake.sh --wide-strings sets the CMake option CSTRING_USE_WIDE_STRINGS, which defines the macro on the library and everything that links it. Examples and tests include cstring.helpers.h for CSTRING_T_(), CSTRING_STRCMP_(), and CSTRING_STRNCMP_(). That header is not part of the library contract. Print a payload with cstring_write() / cstring_writeline();
  • Custom arenas (CSTRING_F_USE_CUSTOMARENAFUNCTIONS) are declared and return CSTRING_RC_CUSTOMARENANOTSUPPORTED. CSTRING_F_MEMORY_IS_OFFSET is set by the implementation and is not a client mode.

Installation

Detailed instructions - via CMake, via bundling - are provided in the accompanying INSTALL.md file.

Components

Types

The C API is based around two structures:

  • cstring_t, which represents a resizeable string instance; and
    struct cstring_t
    {
      size_t          len;        /*!< Number of characters.                              */
      cstring_char_t* ptr;        /*!< Pointer to the string. If capacity is 0, the value
                                       of this member is undetermined.                    */
      size_t          capacity;   /*!< Number of bytes available.                         */
      cstring_flags_t flags;      /*!< Flags. This field belongs to the implementation,
                                       and must not be modified by any application code.  */
    };
  • cstring_vector_t, which represents a sequence of cstring_t instances;
    struct cstring_vector_t
    {
      size_t          len;        /*!< Number of strings.                                 */
      cstring_t*      ptr;        /*!< Pointer to the first string. If capacity is 0, the
                                       value of this member is undetermined.              */
      size_t          capacity;   /*!< Number of instances available.                     */
      cstring_flags_t flags;      /*!< Flags. This field belongs to the implementation,
                                       and must not be modified by any application code.  */
    };

Supporting scalar typedefs:

  • cstring_char_t — character type (char, or wchar_t when CSTRING_USE_WIDE_STRINGS is defined);
  • cstring_flags_t — bit flags controlling allocation and capacity semantics;
  • cstring_hash_t — 64-bit unsigned integer type (uint64_t) representing hash values;

Constants

  • CSTRING_VER — the composite library version;
  • cstring_t_DEFAULT — { 0, NULL, 0, 0 }, an uninitialised cstring_t. cstring_init() assigns this;
  • cstring_vector_t_DEFAULT — the same shape for a cstring_vector_t;
  • cstring_vector_DEFAULT_CAPACITY — sentinel (~(size_t)0) passed to creators so the implementation chooses the capacity;
  • CSTRING_FROM_END(x) — reverse index for cstring_insert(), cstring_insertLen(), cstring_replace(), and cstring_replaceLen();
  • CSTRING_HASH_DJB2_SEED — djb2 initial seed (5381); also the hash of an empty input;
  • CSTRING_HASH_FNV1A_OFFSET — FNV-1a 64-bit offset basis (0xcbf29ce484222325ULL); also the hash of an empty input;
  • CSTRING_HASH_FNV1A_PRIME — FNV-1a 64-bit prime (0x100000001b3ULL);
  • CSTRING_HASH_SDBM_MULTIPLIER — sdbm multiplier (65599);
  • CSTRING_HASH_SDBM_SEED — sdbm initial seed (0); also the hash of an empty input;

String API

Defined in cstring/cstring.h:

Status and capacity

  • cstring_getStatusCodeString() — returns a NUL-terminated description of a CSTRING_RC code;
  • cstring_getStatusCodeStringLength() — returns the length of that description, or 0 if the code is not recognised;
  • cstring_setCapacity() — adjusts capacity (subject to fixed / borrowed / readonly rules);
  • cstring_yield2() — yields ownership of the payload (and raw buffer) to the caller;

Creation/destruction functions

  • cstring_init() — initialises an instance to default values (and does not require a following call to cstring_destroy());
  • cstring_create() — creates an instance from a C-style string;
  • cstring_createLen() — creates an instance from a (portion of a) C-style string;
  • cstring_createN() — creates an instance from a number of repetitions of a character value;
  • cstring_createEx() — creates an instance with special characteristics (borrowed buffer, allocator flags, …);
  • cstring_createLenEx() — as cstring_createEx(), from a fixed number of characters;
  • cstring_destroy() — releases resources and resets the instance;

Modification functions

  • cstring_assign() — assigns a C-style string (may reallocate);
  • cstring_assignLen() — assigns a fixed character count (embedded NULs allowed);
  • cstring_copy() — copies one cstring_t into another;
  • cstring_append() — appends a C-style string;
  • cstring_appendLen() — appends a fixed character count;
  • cstring_insert() — inserts a C-style string at an index (CSTRING_FROM_END supported);
  • cstring_insertLen() — inserts a fixed character count at an index;
  • cstring_replace() — replaces a section at an index with a C-style string;
  • cstring_replaceLen() — replaces a section at an index with a fixed character count;
  • cstring_replaceAll() — replaces all occurrences of one substring with another;
  • cstring_truncate() — shortens the logical length (capacity unchanged);
  • cstring_swap() — swaps the contents of two instances;

Comparison functions

  • cstring_equal() — non-zero when two strings hold the same code units for len. A NULL pointer and a zero length are empty. capacity and flags are ignored;
  • cstring_compare() — negative, zero, or positive order of those same code units. Test equality with cstring_equal();
  • C++ operator== and operator!= call cstring_equal(); operator< calls cstring_compare();

File functions

  • cstring_readline() — reads a line of text from the given text stream into the instance;
  • cstring_write() — writes the string to the given text stream;
  • cstring_writeline() — writes the string followed by a newline to the given text stream;

Hashing functions

Declared in cstring/hash.h, which cstring.h includes. The three algorithms return cstring_hash_t (uint64_t). Multibyte and wide entry points exist in every build, whatever ambient cstring_char_t is. A multibyte code unit contributes its one octet. A wide code unit contributes every octet of the wchar_t, low byte first, so "a" and L"a" differ. The wide value depends on sizeof(wchar_t): two octets on Windows, four on Unix. Big-endian and little-endian hosts of the same width agree. A NULL pointer, or a zero length, yields that algorithm's empty-input value and does not read the pointer. The numeric tables below are the multibyte results.

Suffix Input What is hashed
(none) cstring_t const* ptr for len code units, including embedded NULs
_mbs char const* stops at the first NUL
_wcs wchar_t const* stops at the first NUL
_buf cstring_char_t const*, size_t ambient buffer; calls _mbuf or _wbuf
_mbuf char const*, size_t exactly cch code units, including embedded NULs
_wbuf wchar_t const*, size_t exactly cch code units, including embedded NULs

_case is the last suffix of each name (cstring_hash_djb2_mbs_case(), cstring_hash_fnv1a_wbuf_case(), cstring_hash_sdbm_mbuf_case(), and so on). Case folding maps ASCII A-Z to a-z and leaves every other code unit unchanged, independent of the process locale, and then hashes the octets of the folded code unit. It is not a Unicode case-fold.

Names follow cstring_hash_<algorithm><suffix>, for example cstring_hash_djb2(), cstring_hash_fnv1a_wbuf_case(), and cstring_hash_sdbm_mbuf().

From C++11, std::hash<cstring_t> is the FNV-1a hash of ptr and len, converted to size_t. djb2 and SDBM are not used for that specialisation.

djb2

The hash starts at CSTRING_HASH_DJB2_SEED (5381). For each octet b the step is hash = ((hash << 5) + hash) + b, which is hash * 33 + b. Daniel J. Bernstein's djb2, as published by Ozan Yigit.

Input Hash
empty, or NULL 5381
"a" 177670
"foobar" 6953516687550
{ 'a', 0, 'b' } length 3 193482728
octet 0xFF 177828

The length-3 buffer differs from "ab", because the embedded NUL is hashed. 64-bit djb2 matches 32-bit djb2 only while the running total stays below 2^32. "foobar" is already past that point.

FNV-1a

The hash starts at CSTRING_HASH_FNV1A_OFFSET (0xcbf29ce484222325). For each octet b the step is hash ^= b and then hash *= CSTRING_HASH_FNV1A_PRIME (0x100000001b3). Fowler, Noll, and Vo FNV-1a, 64-bit.

Input Hash
empty, or NULL 0xcbf29ce484222325
"a" 0xaf63dc4c8601ec8c
"foobar" 0x85944171f73967e8
{ 'a', 0, 'b' } length 3 0xe5d29919042666b2
octet 0xFF 0xaf64724c8602eb6e
SDBM

The hash starts at CSTRING_HASH_SDBM_SEED (0). For each octet b the step is hash * CSTRING_HASH_SDBM_MULTIPLIER + b. The multiplier is 65599. This is the sdbm recurrence published by Ozan Yigit, evaluated in a 64-bit accumulator.

Input Hash
empty, or NULL 0
"a" 97
"foobar" 0x430d469aa6437b0d
{ 'a', 0, 'b' } length 3 0x612fc3e043
octet 0xFF 255

The length-3 buffer differs from "ab", because the embedded NUL is hashed.

Vector API

Defined in cstring/cstring.vector.h:

  • cstring_vector_init() — initialises a vector, optionally with a minimum capacity;
  • cstring_vector_create() — creates a vector of a given initial size (elements default-initialised);
  • cstring_vector_destroy() — destroys each element and frees the vector buffer;
  • cstring_vector_truncate() — shortens the vector, destroying trailing elements;
  • cstring_vector_insertAt() — inserts one or more cstring_t instances at a position;
  • cstring_vector_append() / cstring_vector_prepend() — macros over cstring_vector_insertAt();
  • cstring_vector_readLines() — reads lines from a stream into the vector;

C++ Integration

When included in C++ compilation units, cstring/cstring.h provides inline access shims:

  • String access shims — c_str_data(), c_str_len(), and c_str_ptr(), allowing cstring_t instances to be used directly with STLSoft and generic C++ templates;
  • Hash access shims — cstring::hash_djb2(), cstring::hash_djb2_case(), cstring::hash_fnv1a(), cstring::hash_fnv1a_case(), cstring::hash_sdbm(), and cstring::hash_sdbm_case(), overloaded for struct cstring_t const&, struct cstring_t const*, char const*, wchar_t const*, and the buffer forms (char const* s, size_t cch) and (wchar_t const* s, size_t cch). A NULL argument must be cast, because char const* and wchar_t const* are both viable;

Examples

Examples live under examples/ (c/ and cpp/). The directory is the subject; the built program is example.<lang>.<subject>. Each has a short README.md. Build them with BUILD_EXAMPLES (on by default); run via run_all_examples.sh.

Example Language Notes
example.c.cstring C Core cstring_t create / assign / append / truncate / copy / swap
example.c.cstring.auto_buffer C Borrowed buffer that may grow to the heap
example.c.cstring_create C Minimal cstring_create()
example.c.cstring_vector C Read lines into cstring_vector_t and sort (input path or --; SIS_EXAMPLE_SMOKE enables no-arg demo)
example.cpp.cstring.dynload C++ Windows-only dynamic load of the cstring DLL
example.cpp.cstring.global_memory C++ Windows-only CSTRING_F_USE_WINDOWS_GLOBAL_MEMORY

Project Information

Where to get help

Contribution guidelines

Defect reports, feature requests, and pull requests are welcome on https://github.com/synesissoftware/cstring.

See also HOW_YOU_CAN_HELP.md.

Dependencies

The C API has no non-standard dependencies.

Dependency Role Required?
STLSoft 1.11 Test headers / remaining C++ tests and examples ⚪ Tests only (BUILD_TESTING)
xTests (≥ 0.26) Unit / component / scratch tests ⚪ Tests only (BUILD_TESTING)
p99 Percentiles in performance tests; file_lines suite ⚪ Optional; tests only (unless NO_P99 / --no-p99)
shwild Enhanced pattern-match assertions in xTests ⚪ Optional; tests only (unless NO_SHWILD / --no-shwild)

When supplying '--no-cpp' to prepare_cmake.sh — sets the CMake option NO_CSTRING_CPP_API=ON — C++ examples and remaining C++ tests are omitted; the C unit-tests still require STLSoft and xTests.

When supplying '--no-p99' — sets NO_P99=ON — p99 is not recognised; performance tests still build but omit percentiles and the filesystem file_lines suite.

When supplying '--no-shwild' — sets NO_SHWILD=ON — shwild is not recognised and pattern-match assertions are compiled out; other unit-tests still run.

When supplying '--wide-strings' — sets CSTRING_USE_WIDE_STRINGS=ON — cstring_char_t is wchar_t. Examples and unit/component tests follow that type. Performance tests are not built: they compare char payloads with std::string. CI runs that configuration as windows-cl-wide and windows-mingw-wide.

Related projects

Projects that call the cstring API, or that require the CMake package cstring:

Project Use
errni Builds each result line and writes it with cstring_writeline()
lnunique Requires cstring 4.0 and links cstring::core for line strings
rstrip Accumulates each output line and writes it with cstring_write()
shwild.fnmatch Holds patterns and subject strings as cstring_t
STLSoft Optional; the C unit test output_debug_line.C uses cstring_t when the package is found

License

cstring is released under the 3-clause BSD license. See LICENSE for details.

Releases

Packages

Used by

Contributors

Languages