Skip to content

Commit ea258c1

Browse files
committed
Add: Branchless .empty() for small strings
Relates to #288
1 parent 024f677 commit ea258c1

3 files changed

Lines changed: 28 additions & 5 deletions

File tree

include/stringzilla/small_string.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,21 @@ typedef union sz_string_t {
9595

9696
} sz_string_t;
9797

98+
/*
99+
* Verify structure layout for branchless length extraction.
100+
* On little-endian: internal.length (8-bit) overlaps with LSB of external.length at offset 8.
101+
* On big-endian: internal.length is at offset 31, external.length at offset 24.
102+
*/
98103
#if !SZ_AVOID_LIBC // `offsetof` comes from `stddef.h`, which is part of the C standard library.
99104
sz_static_assert(offsetof(sz_string_t, internal.start) == offsetof(sz_string_t, external.start),
100105
Alignment_confusion_between_internal_and_external_storage);
106+
#if !SZ_IS_BIG_ENDIAN_
107+
sz_static_assert(offsetof(sz_string_t, internal.length) == 8, Internal_length_offset_mismatch_on_little_endian);
108+
sz_static_assert(offsetof(sz_string_t, external.length) == 8, External_length_offset_mismatch_on_little_endian);
109+
#else
110+
sz_static_assert(offsetof(sz_string_t, internal.length) == 31, Internal_length_offset_mismatch_on_big_endian);
111+
sz_static_assert(offsetof(sz_string_t, external.length) == 24, External_length_offset_mismatch_on_big_endian);
112+
#endif
101113
#endif
102114

103115
#pragma endregion // Core Structure
@@ -138,6 +150,11 @@ SZ_PUBLIC void sz_string_unpack( //
138150
*/
139151
SZ_PUBLIC void sz_string_range(sz_string_t const *string, sz_ptr_t *start, sz_size_t *length);
140152

153+
/**
154+
* @brief Branchless check if the string is empty.
155+
*/
156+
SZ_PUBLIC sz_bool_t sz_string_is_empty(sz_string_t const *string);
157+
141158
/**
142159
* @brief Constructs a string of a given ::length with noisy contents.
143160
* Use the returned character pointer to populate the string.
@@ -220,6 +237,12 @@ SZ_PUBLIC void sz_string_range(sz_string_t const *string, sz_ptr_t *start, sz_si
220237
*length = string->external.length & (0x00000000000000FFull | is_big_mask);
221238
}
222239

240+
SZ_PUBLIC sz_bool_t sz_string_is_empty(sz_string_t const *string) {
241+
sz_size_t is_small = (sz_cptr_t)string->internal.start == (sz_cptr_t)&string->internal.chars[0];
242+
sz_size_t is_big_mask = is_small - 1ull;
243+
return (sz_bool_t)((string->external.length & (0x00000000000000FFull | is_big_mask)) == 0);
244+
}
245+
223246
SZ_PUBLIC void sz_string_unpack( //
224247
sz_string_t const *string, sz_ptr_t *start, sz_size_t *length, sz_size_t *space, sz_bool_t *is_external) {
225248
sz_size_t is_small = (sz_cptr_t)string->internal.start == (sz_cptr_t)&string->internal.chars[0];

include/stringzilla/stringzilla.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2833,7 +2833,7 @@ class basic_string {
28332833
size_type size() const noexcept { return view().size(); }
28342834
size_type length() const noexcept { return size(); }
28352835
size_type max_size() const noexcept { return npos - 1; }
2836-
bool empty() const noexcept { return string_.external.length == 0; }
2836+
bool empty() const noexcept { return sz_string_is_empty(&string_); }
28372837
size_type capacity() const noexcept {
28382838
sz_ptr_t string_start;
28392839
sz_size_t string_length;

include/stringzilla/types.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,10 @@
104104
* Fall back to legacy macros and known arch tags when unavailable.
105105
*/
106106
#if !defined(SZ_IS_BIG_ENDIAN_)
107-
#if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) || \
108-
(defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)) || defined(__BIG_ENDIAN__) || defined(_BIG_ENDIAN) || \
109-
defined(BIG_ENDIAN) || defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || defined(_MIBSEB) || \
110-
defined(__MIBSEB) || defined(__MIBSEB__) || defined(__s390x__) || defined(__s390__)
107+
#if (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) || \
108+
(defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)) || defined(__BIG_ENDIAN__) || defined(_BIG_ENDIAN) || \
109+
defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || defined(_MIBSEB) || defined(__MIBSEB) || \
110+
defined(__MIBSEB__) || defined(__s390x__) || defined(__s390__)
111111
#define SZ_IS_BIG_ENDIAN_ (1) //< It's a big-endian target architecture
112112
#else
113113
#define SZ_IS_BIG_ENDIAN_ (0) //< It's a little-endian target architecture

0 commit comments

Comments
 (0)