Skip to content

Repository files navigation

StormByte-String

Platform C++26 CMake License: LGPL v3 or commercial CI Sponsor

This repository is StormByte-String.

It is not a replacement for std::string, std::wstring or ICU. Those already do their job better inside a single binary.

The point of this library is a small, owned string that is safe to return and store across a DLL / shared-object boundary. std::string / std::wstring are not: allocator, layout and CRT can differ on each side of the link on Windows. String / WString own a CString / WCString allocated by this module. Views (string_view, iterators, data()) and copies into std::string / std::wstring are inline in the caller, so the caller’s heap is the caller’s heap.

If the text never leaves the module that created it, use std::string.

It depends on StormByte (Base) 2.0.0 or later.

What this module does

  • String — UTF-8 text on CString. Contiguous const iterators, implicit std::string_view, explicit std::string, operator<<.
  • WString — wide text on WCString. Same shape with wchar_t / std::wstring_view / std::wstring / std::wostream.
  • Conversion — explicit String ↔ WString (UTF-8; ill-formed input becomes U+FFFD).
  • Ordering — content == / != / <=>, swap, std::hash. A default object is null; "" / L"" is valid empty text. Null is not equal to empty.
  • Algorithms — begin / end / data / size so <algorithm> and std::ranges run on the object.
  • Serialization — Serializable<String> and Serializable<WString> write the same little-endian wire as std::string / std::wstring. The blob is a StormByte::BinaryData.

On top of that, the types carry operations that show up constantly when text crosses a module boundary: ToUpper / ToLower, SanitizeNewlines, RemoveWhitespace, IsInteger, Split and Explode. Each one exists as a static and as an instance method. That list is not a Unicode toolkit and is not frozen; later releases can add more of the same kind.

ToUpper / ToLower map ASCII and Latin-1 Supplement. Any other code point is copied as-is.

The rest of the suite

Module Role API
Base Foundation every other module links /StormByte
Buffer FIFO, SharedFIFO, Ring, Producer/Consumer and multi-stage pipelines /StormByte-Buffer
Config Human-readable text and versioned binary documents (groups, lists, raw bytes) /StormByte-Config
Crypto Hash, compress, encrypt, sign and key agreement — Crypto++ never leaves the private tree /StormByte-Crypto
Database One API over SQLite, PostgreSQL and MariaDB /StormByte-Database
Logger Stream logger with levels, headers, human-readable sizes and redaction (ThreadedLog) /StormByte-Logger
Multimedia Decode, encode and containers without raw FFmpeg types; codecs enabled only if present /StormByte-Multimedia
Network Framed packets, Client/Server, IPv4/IPv6 TCP and Buffer pipelines (compress/encrypt) /StormByte-Network
String This repository /StormByte-String
System Processes, pipes and environment variables across Linux, Windows and macOS /StormByte-System

Table of Contents

Installation

Needs a C++26 compiler, CMake 3.28 or newer, and Base 2.0.0.

git clone --recurse-submodules https://github.com/StormBytePP/StormByte-String.git
cd StormByte-String
cmake -S . -B build
cmake --build build

Shared vs static follows CMake BUILD_SHARED_LIBS (declared in lib/, default ON). A plain configure builds the shared library. -DBUILD_SHARED_LIBS=OFF builds a static archive; on Windows the headers then do not use dllimport. The bundled Base tree follows the same mode.

A shared build keeps this library as its own .so / .dll. Under the LGPL that is usually the simpler way to ship: the user can replace that file. A static archive is folded into your binary. The LGPL still applies to this code; you must give the recipient a way to relink your product with a different build of this library. If that does not fit how you distribute the final product, a commercial license is available from the copyright holder (see License).

Headers:

#include <StormByte/string/string.hxx>
#include <StormByte/string/wstring.hxx>
#include <StormByte/string/serializable.hxx>

Usage

Namespace root for the types is StormByte::String.

A default String / WString is null (operator bool is false, data() is null). Constructed from "" / L"" it is valid and empty.

String

#include <StormByte/string/string.hxx>
#include <iostream>
#include <string>

using StormByte::String::String;

int main() {
	String text("hello");
	if (text)
		std::cout << text << " " << text.size() << std::endl;

	const std::string_view view = text;
	const std::string copy = text;

	String missing;
	String empty("");
	if (!missing && empty && missing != empty)
		std::cout << "null is not empty" << std::endl;
}

WString

#include <StormByte/string/wstring.hxx>
#include <iostream>

using StormByte::String::WString;

int main() {
	WString text(L"wide");
	std::wcout << text << L" " << text.size() << std::endl;
}

Conversion

#include <StormByte/string/string.hxx>
#include <StormByte/string/wstring.hxx>
#include <iostream>

using StormByte::String::String;
using StormByte::String::WString;

int main() {
	String utf8("café");
	WString wide(utf8);
	String back(wide);
	WString also = static_cast<WString>(utf8);
	if (back == utf8 && also == wide)
		std::cout << "round-trip" << std::endl;
}

Case

#include <StormByte/string/string.hxx>
#include <iostream>

using StormByte::String::String;

int main() {
	std::cout << String::ToUpper("café") << std::endl;
	std::cout << String("CAFÉ").ToLower() << std::endl;
}

That prints CAFÉ and café. ß, Greek, Cyrillic and anything outside Latin-1 stay unchanged.

Newlines and whitespace

#include <StormByte/string/string.hxx>
#include <iostream>

using StormByte::String::String;

int main() {
	std::cout << String::SanitizeNewlines("a\r\nb\n") << std::endl;
	std::cout << String(" a\tb\n").RemoveWhitespace() << std::endl;
}

SanitizeNewlines turns CR LF into LF. Other bytes are kept. RemoveWhitespace drops what isspace / iswspace reports.

Integer

#include <StormByte/string/string.hxx>
#include <iostream>

using StormByte::String::String;

int main() {
	std::cout << String::IsInteger("42") << " "
	          << String("-3").IsInteger() << " "
	          << String::IsInteger("1a") << std::endl;
}

An optional leading + / - plus digits. Empty, "-", whitespace and letters fail.

Split

Whitespace-separated tokens. Leading and trailing space is skipped. The output container is the caller’s.

#include <StormByte/string/string.hxx>
#include <iostream>
#include <vector>

using StormByte::String::String;

int main() {
	std::vector<String> tokens;
	String::Split("  a  bb\tc ", tokens);
	for (const String& token : tokens)
		std::cout << token << std::endl;

	const auto also = String("x y").Split();
}

Explode

Tokens on a delimiter. Empty fields stay in the queue.

#include <StormByte/string/string.hxx>
#include <iostream>
#include <queue>

using StormByte::String::String;

int main() {
	std::queue<String> parts;
	String::Explode("a,,b", ',', parts);
	while (!parts.empty()) {
		std::cout << "[" << parts.front() << "]" << std::endl;
		parts.pop();
	}
}

That prints [a], [], [b].

Algorithms

#include <StormByte/string/string.hxx>
#include <algorithm>
#include <iostream>
#include <ranges>

using StormByte::String::String;

int main() {
	const String text("mississippi");
	if (std::ranges::find(text, 'p') != text.end())
		std::cout << std::ranges::count(text, 'i') << std::endl;

	const std::string_view view = text;
	if (std::ranges::equal(view, std::string_view("mississippi")))
		std::cout << "view" << std::endl;
}

WString is the same with wchar_t literals (L"…", L',', std::wstring_view).

Serialization

String and WString plug into Base’s Serializable<T>. Include <StormByte/string/serializable.hxx>.

The wire is the same as std::string / std::wstring: little-endian uint64 byte count, then raw UTF-8. A null String / WString is written as an empty payload. Decode always yields a non-null buffer ("" / L"" when the payload is empty).

Serialize() returns a StormByte::BinaryData. That blob lives on Base’s heap, so it can cross a DLL boundary. Do not put std::vector<std::byte> in a public signature for the same job.

#include <StormByte/binary_data.hxx>
#include <StormByte/string/serializable.hxx>
#include <StormByte/string/string.hxx>
#include <StormByte/string/wstring.hxx>
#include <iostream>

using StormByte::BinaryData;
using StormByte::Serializable;
using StormByte::String::String;
using StormByte::String::WString;

int main() {
	const String text("StormByte");
	const BinaryData blob = Serializable<String>(text).Serialize();
	auto back = Serializable<String>::Deserialize(blob);
	if (back && back.value() == text)
		std::cout << back.value() << std::endl;

	const WString wide(L"StormByte");
	const BinaryData same_wire = Serializable<WString>(wide).Serialize();
	if (blob == same_wire)
		std::cout << "UTF-8 wire matches" << std::endl;
}

Serializable<String>("StormByte") and Serializable<std::string>("StormByte") produce the same bytes. The same holds for WString and std::wstring.

Contributing

Issues and pull requests belong on this repository. Fork and open a PR against master.

Read CONTRIBUTING.md before you send a patch (copyright assignment and review rules). Coding rules are in CODING_STYLE.md.

License

Since 1.0.0, original source in this repository is dual-licensed: GNU Lesser General Public License v3 or later, or a commercial license from the copyright holder (David C. Manuelda StormByte@gmail.com).

The grant applies only to original StormByte-String source in this repository. It does not cover other StormByte modules or third-party material shipped here (including everything under thirdparty/, and in particular the bundled StormByte Base tree), which remains under its own license. Neither license grants patent rights.

See LICENSE for the dual-license notice and COPYING.LGPLv3 for the full GNU LGPL version 3 text. Also https://www.gnu.org/licenses/lgpl-3.0.html.

Static linking under the LGPL is described under Installation.

Support

StormByte is developed in spare time. Sponsorship is optional and does not buy features, priority or support.

About

C++26 string module of the StormByte suite. DLL-safe text on top of Base CString/WCString: views, conversions and helpers without crossing std::string across the shared-library boundary.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages