Skip to content

Commit bde1fad

Browse files
committed
Fix: Generalize static asserts to 32-bit archs
1 parent 7e3dd35 commit bde1fad

1 file changed

Lines changed: 28 additions & 16 deletions

File tree

include/stringzilla/small_string.h

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -97,18 +97,30 @@ typedef union sz_string_t {
9797

9898
/*
9999
* 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.
100+
* On little-endian 64-bit: internal.length (8-bit) overlaps with LSB of external.length at offset 8.
101+
* On little-endian 32-bit: internal.length (8-bit) overlaps with LSB of external.length at offset 4.
102+
* On big-endian 64-bit: internal.length is at offset 31, external.length at offset 24.
103+
* On big-endian 32-bit: internal.length is at offset 15, external.length at offset 12.
102104
*/
103105
#if !SZ_AVOID_LIBC // `offsetof` comes from `stddef.h`, which is part of the C standard library.
104106
sz_static_assert(offsetof(sz_string_t, internal.start) == offsetof(sz_string_t, external.start),
105107
Alignment_confusion_between_internal_and_external_storage);
106108
#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+
#if SZ_IS_64BIT_
110+
sz_static_assert(offsetof(sz_string_t, internal.length) == 8, Internal_length_offset_mismatch_on_little_endian_64);
111+
sz_static_assert(offsetof(sz_string_t, external.length) == 8, External_length_offset_mismatch_on_little_endian_64);
109112
#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);
113+
sz_static_assert(offsetof(sz_string_t, internal.length) == 4, Internal_length_offset_mismatch_on_little_endian_32);
114+
sz_static_assert(offsetof(sz_string_t, external.length) == 4, External_length_offset_mismatch_on_little_endian_32);
115+
#endif
116+
#else // SZ_IS_BIG_ENDIAN_
117+
#if SZ_IS_64BIT_
118+
sz_static_assert(offsetof(sz_string_t, internal.length) == 31, Internal_length_offset_mismatch_on_big_endian_64);
119+
sz_static_assert(offsetof(sz_string_t, external.length) == 24, External_length_offset_mismatch_on_big_endian_64);
120+
#else
121+
sz_static_assert(offsetof(sz_string_t, internal.length) == 15, Internal_length_offset_mismatch_on_big_endian_32);
122+
sz_static_assert(offsetof(sz_string_t, external.length) == 12, External_length_offset_mismatch_on_big_endian_32);
123+
#endif
112124
#endif
113125
#endif
114126

@@ -231,26 +243,26 @@ SZ_PUBLIC sz_bool_t sz_string_is_on_stack(sz_string_t const *string) {
231243

232244
SZ_PUBLIC void sz_string_range(sz_string_t const *string, sz_ptr_t *start, sz_size_t *length) {
233245
sz_size_t is_small = (sz_cptr_t)string->internal.start == (sz_cptr_t)&string->internal.chars[0];
234-
sz_size_t is_big_mask = is_small - 1ull;
246+
sz_size_t is_big_mask = is_small - (sz_size_t)1;
235247
*start = string->external.start; // It doesn't matter if it's on stack or heap, the pointer location is the same.
236-
// If the string is small, use branch-less approach to mask-out the top 7 bytes of the length.
237-
*length = string->external.length & (0x00000000000000FFull | is_big_mask);
248+
// If the string is small, use branch-less approach to mask-out the top bytes of the length.
249+
*length = string->external.length & ((sz_size_t)0xFF | is_big_mask);
238250
}
239251

240252
SZ_PUBLIC sz_size_t sz_string_length(sz_string_t const *string) {
241253
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 string->external.length & (0x00000000000000FFull | is_big_mask);
254+
sz_size_t is_big_mask = is_small - (sz_size_t)1;
255+
return string->external.length & ((sz_size_t)0xFF | is_big_mask);
244256
}
245257

246258
SZ_PUBLIC void sz_string_unpack( //
247259
sz_string_t const *string, sz_ptr_t *start, sz_size_t *length, sz_size_t *space, sz_bool_t *is_external) {
248260
sz_size_t is_small = (sz_cptr_t)string->internal.start == (sz_cptr_t)&string->internal.chars[0];
249-
sz_size_t is_big_mask = is_small - 1ull;
261+
sz_size_t is_big_mask = is_small - (sz_size_t)1;
250262
*start = string->external.start; // It doesn't matter if it's on stack or heap, the pointer location is the same.
251-
// If the string is small, use branch-less approach to mask-out the top 7 bytes of the length.
252-
*length = string->external.length & (0x00000000000000FFull | is_big_mask);
253-
// In case the string is small, the `is_small - 1ull` will become 0xFFFFFFFFFFFFFFFFull.
263+
// If the string is small, use branch-less approach to mask-out the top bytes of the length.
264+
*length = string->external.length & ((sz_size_t)0xFF | is_big_mask);
265+
// In case the string is small, the `is_small - (sz_size_t)1` will become all-ones mask.
254266
*space = sz_u64_blend(SZ_STRING_INTERNAL_SPACE, string->external.space, is_big_mask);
255267
*is_external = (sz_bool_t)!is_small;
256268
}
@@ -408,7 +420,7 @@ SZ_PUBLIC sz_ptr_t sz_string_expand( //
408420
}
409421
// If we are not lucky, we need to allocate more memory.
410422
else {
411-
sz_size_t next_planned_size = sz_max_of_two(SZ_CACHE_LINE_WIDTH, string_space * 2ull);
423+
sz_size_t next_planned_size = sz_max_of_two(SZ_CACHE_LINE_WIDTH, string_space * (sz_size_t)2);
412424
sz_size_t min_needed_space = sz_size_bit_ceil(offset + string_length + added_length + 1);
413425
sz_size_t new_space = sz_max_of_two(min_needed_space, next_planned_size);
414426
string_start = sz_string_reserve(string, new_space - 1, allocator);

0 commit comments

Comments
 (0)