CX Framework
Cross-platform C utility framework
Loading...
Searching...
No Matches
string_private.h
1#pragma once
2
3#include "cx/string.h"
4#include "cx/thread/atomic.h"
5#include "cx/utils/compare.h"
6
7typedef struct str_ref* _Nonnull string_v; // validated string
8typedef const struct str_ref* _Nonnull strref_v; // validated strref
9typedef string_v* _Nonnull strhandle_v; // handle to validated string
10
11// Flags field
12
13#define STR_LEN0 0x00 // no length field, 8-bit ref count if STR_ALLOC
14#define STR_LEN8 0x01 // 8-bit length, 8-bit ref count if STR_ALLOC
15#define STR_LEN16 0x02 // 16-bit length, 16-bit ref count if STR_ALLOC
16#define STR_LEN32 0x03 // 32-bit length, 16-bit ref count if STR_ALLOC
17#define STR_LEN_MASK 0x03
18
19#define STR_STACK 0x04 // string is allocated on the stack, ref count is buffer size
20#define STR_ROPE 0x08 // string is a rope node
21#define STR_ALLOC 0x10 // string allocated by us, ref count field present
22#define STR_UTF8 0x20 // string is UTF-8
23#define STR_ASCII 0x40 // string is 7-bit ASCII
24#define STR_CX 0x80 // string contains cx header, otherwise plain C string
25
26#define STR_ENCODING_MASK (STR_UTF8 | STR_ASCII)
27
28#define STR_HDRP(s) ((uint8*)(s))
29#define STR_HDR(s) (((*STR_HDRP(s) & STR_CX) && STR_HDRP(s)[1] == 0xc1) ? *STR_HDRP(s) : 0)
30#define STR_FIELD(s, off, typ) (*(typ*)(&STR_HDRP(s)[off]))
31
32#define STR_OT_LEN 0
33#define STR_OT_REF 1
34#define STR_OT_STR 2
35extern const uint8 _str_off[32];
36// dirty trick here, since STR_ALLOC is defined as 16, it can be used
37// as-is as a base index into the table without further masking or shifting
38#define STR_OFF_BASE(hdr) ((hdr & STR_ALLOC) | (hdr & STR_LEN_MASK) << 2)
39#define STR_OFF_LEN(hdr) (_str_off[STR_OFF_BASE(hdr)])
40#define STR_OFF_REF(hdr) (_str_off[STR_OFF_BASE(hdr) | STR_OT_REF])
41#define STR_OFF_STR(hdr) (hdr & STR_CX ? (_str_off[STR_OFF_BASE(hdr) | STR_OT_STR]) : 0)
42
43#define STR_LEN8_LEN(s) STR_FIELD(s, _strOffLen(_strHdr(s)), uint8)
44#define STR_LEN16_LEN(s) STR_FIELD(s, _strOffLen(_strHdr(s)), uint16)
45#define STR_LEN32_LEN(s) STR_FIELD(s, _strOffLen(_strHdr(s)), uint32)
46
47#define STR_LEN8_REF(s) STR_FIELD(s, _strOffRef(_strHdr(s)), uint8)
48#define STR_LEN16_REF(s) STR_FIELD(s, _strOffRef(_strHdr(s)), uint16)
49
50#define STR_BUFFER(s) (&STR_FIELD(s, _strOffStr(_strHdr(s)), uint8))
51#define STR_ROPEDATA(s) (&STR_FIELD(s, _strOffStr(_strHdr(s)), str_ropedata))
52
53_meta_inline uint8 _strOffLen(uint8 hdr)
54{
55 return STR_OFF_LEN(hdr);
56}
57
58_meta_inline uint8 _strOffRef(uint8 hdr)
59{
60 return STR_OFF_REF(hdr);
61}
62
63_meta_inline uint8 _strOffStr(uint8 hdr)
64{
65 return STR_OFF_STR(hdr);
66}
67
68_Ret_valid_ _meta_inline uint8* _Nonnull _strHdrP(_In_ strref_v s)
69{
70 return STR_HDRP(s);
71}
72
73_meta_inline uint8 _strHdr(_In_ strref_v s)
74{
75 return STR_HDR(s);
76}
77
78_Ret_valid_ _meta_inline uint8* _Nonnull _strBuffer(_In_ strref_v s)
79{
80 return STR_BUFFER(s);
81}
82
83#define STR_CHECK_VALID(s) (s && *(uint8*)s)
84#define STR_SAFE_DEREF(ps) ((ps && *ps) ? *ps : 0)
85
86_meta_inline uint32 _strFastLen(_In_ strref_v s)
87{
88 if (!(STR_HDR(s) & STR_CX))
89 return (uint32)cstrLen((const char*)s);
90 switch (STR_HDR(s) & STR_LEN_MASK) {
91 case STR_LEN8:
92 return STR_LEN8_LEN(s);
93 case STR_LEN16:
94 return STR_LEN16_LEN(s);
95 case STR_LEN32:
96 return STR_LEN32_LEN(s);
97 default: // STR_LEN0
98 return (uint32)cstrLen((const char*)_strBuffer(s));
99 }
100}
101
102// Content buffer of a string with no embedded length field -- plain C strings, _S""
103// literals, and _SL() on MSVC. These are always flat and NUL terminated, since ropes
104// are always STR_LEN32 and _strSetLen cannot represent STR_LEN0 at all. Returns NULL
105// if the string carries a real length field.
106_Ret_maybenull_ _meta_inline uint8* _Nullable _strLen0Buf(_In_ strref_v s)
107{
108 // STR_HDR is 0 for a plain C string, so 0 & STR_LEN_MASK == STR_LEN0 covers those too
109 if ((STR_HDR(s) & STR_LEN_MASK) != STR_LEN0)
110 return NULL;
111
112 return _strBuffer(s); // resolves to s itself when STR_CX is absent
113}
114
115// must only be used on STR_CX | STR_ALLOC strings!
116_meta_inline uint16 _strFastRef(_In_ strref_v s)
117{
118 int l = STR_HDR(s) & STR_LEN_MASK;
119
120 if (l <= STR_LEN8)
121 return atomicLoad(uint8, &STR_FIELD(s, STR_OFF_REF(STR_HDR(s)), atomic(uint8)), Acquire);
122 else
123 return atomicLoad(uint16, &STR_FIELD(s, STR_OFF_REF(STR_HDR(s)), atomic(uint16)), Acquire);
124}
125
126_meta_inline uint16 _strFastRefNoSync(_In_ strref_v s)
127{
128 int l = STR_HDR(s) & STR_LEN_MASK;
129
130 if (l <= STR_LEN8)
131 return STR_LEN8_REF(s);
132 else
133 return STR_LEN16_REF(s);
134}
135
136// Always round up allocations to avoid constant realloc
137#define STR_ALLOC_SIZE 16
138
139// rope tuning
140// try not to create a rope smaller than this
141#define ROPE_MIN_SIZE 64
142// use a rope if final join size exceeds this
143#define ROPE_JOIN_THRESH 128
144// use a rope if substring size exceeds this
145#define ROPE_SUBSTR_THRESH 96
146// maximum size to merge together on joins
147#define ROPE_MAX_MERGE 256
148
149// compare tuning
150// Below this size, walking a length-free string NUL-terminated always beats measuring
151// it first. Above it, comparing two strings that share a very long common prefix can
152// lose to the measure-then-memcmp path, so fall back. Not empirically tuned; the intent
153// is only to bound the pathological case, which needs a LEN0 string this large to begin
154// with (_SLL on MSVC, or a large char buffer passed as a strref).
155#define STR_LEN0_SCAN_THRESH 1024
156
157#include "string_private_utf8.h"
158
159// dummy empty string used as input when NULL or invalid strings are passed in
160extern string_v _strEmpty;
161
162// resets the string internals, re-use memory if possible
163void _strReset(_Inout_ptr_opt_ strhandle s, uint32 minsz);
164
165// these change the structure internally and can result in inconsistent state
166// if not used with care
167void _strSetLen(_Inout_ string_v s, uint32 len);
168void _strInitRef(_Inout_ string_v s);
169void _strSetRef(_Inout_ string_v s, uint16 ref);
170
171// ensure that ps is allocated by us and has a single reference
172void _strMakeUnique(_Inout_ptr_ strhandle_v ps, uint32 minszforcopy);
173// like _strMakeUnique but also flattens ropes into plain strings
174void _strFlatten(_Inout_ptr_ strhandle_v ps, uint32 minszforcopy);
175// resize ps in place if possible, or copy if necessary (changing ps).
176// resizes buffer only, does NOT zero buffer or set length header
177void _strResize(_Inout_ptr_ strhandle_v ps, uint32 len, bool unique);
178// duplicates s and returns a copy, optionally with more reserved space allocated
179_Ret_valid_ string_v _strCopy(_In_ strref_v s, uint32 minsz);
180// direct copy of string buffer or rope internals, does not check destination size!
181uint32 _strFastCopy(_In_ strref_v s, uint32 off, _Out_writes_bytes_(bytes) uint8* _Nonnull buf,
182 uint32 bytes);
183
184// rope data comes directly after string header if STR_ROPE is set
185typedef struct str_roperef {
186 string str;
187 uint32 off;
188 uint32 len;
189} str_roperef;
190typedef struct str_ropedata {
191 str_roperef left;
192 str_roperef right;
193 int depth;
194} str_ropedata;
195
196_Ret_valid_ _meta_inline str_ropedata* _strRopeData(_In_ strref_v s)
197{
198 return STR_ROPEDATA(s);
199}
200
201string_v _strCreateRope(_In_ strref_v left, uint32 left_off, uint32 left_len, _In_opt_ strref right,
202 uint32 right_off, uint32 right_len, bool balance);
203string_v _strCreateRope1(_In_ strref_v s, uint32 off, uint32 len);
204string_v _strCloneRope(_In_ strref_v s);
205void _strDestroyRope(_Inout_ string_v s);
206uint32 _strRopeFastCopy(_In_ strref_v s, uint32 off, _Out_writes_bytes_(bytes) uint8* _Nonnull buf,
207 uint32 bytes);
208_Success_(return) bool _strRopeRealStr(_Inout_ strhandle_v s, uint32 off, _Out_ strhandle_v rs,
209 _Out_ uint32* _Nonnull rsoff, _Out_ uint32* _Nonnull rslen,
210 _Out_ uint32* _Nonnull rsstart, bool writable);
211
212// Resolve a caller-supplied index to a single byte (or code point) position: strEnd is
213// the end and negative counts back from the end, clamping at the start.
214_Pure _meta_inline uint32 _strResolvePos(uint32 slen, int32 i)
215{
216 if (i == strEnd)
217 return slen;
218
219 // need to handle unsigned wraparound for the negative case
220 if (i < 0)
221 return ((uint32)(-i) < slen) ? (uint32)((int32)slen + i) : 0;
222 return (uint32)i;
223}
224
225// Canonical resolution of a caller-supplied byte (or code point) index against a length:
226// strEnd is the end, negative counts back from the end, and everything is clamped into
227// [0, slen].
228_Pure _meta_inline uint32 _strResolveOff(uint32 slen, int32 i)
229{
230 return min(_strResolvePos(slen, i), slen);
231}
232
233// inline helper for case insensitive variants that optimizes to nothing if ci is provably false
234_meta_inline uint8 _strChrFold(uint8 c, bool ci)
235{
236 return ci ? (uint8)tolower(c) : c;
237}
238
239_meta_inline uint8 _strFastChar(_In_ strref s, uint32 i)
240{
241 if (!(STR_HDR(s) & STR_ROPE)) {
242 return _strBuffer(s)[i];
243 } else {
244 string realstr;
245 uint32 realoff, reallen, realstart;
246 if (_strRopeRealStr((string*)&s, i, &realstr, &realoff, &reallen, &realstart, false))
247 return _strBuffer(realstr)[realoff];
248 }
249 return 0;
250}
251
252extern char _strnum_udigits[];
253extern char _strnum_ldigits[];
Atomic operations.
Comparison and clamping macros.
size_t cstrLen(const char *s)
string * strhandle
Pointer to a string variable.
Definition strbase.h:64
Copy-on-write strings with automatic memory management and rope optimization.