1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 Google LLC. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 // An attempt to provide some of the C++ string functionality in C.
9 // Function names generally match those of corresponding C++ string methods.
10 // All buffers are copied so operations are relatively expensive.
11 // Internal character strings are always NULL-terminated.
12 // All bool functions return true on success, false on failure.
13
14 #ifndef UPB_IO_STRING_H_
15 #define UPB_IO_STRING_H_
16
17 #include <stdarg.h>
18 #include <stdlib.h>
19 #include <string.h>
20
21 #include "upb/mem/arena.h"
22 #include "upb/port/vsnprintf_compat.h"
23
24 // Must be last.
25 #include "upb/port/def.inc"
26
27 #ifdef __cplusplus
28 extern "C" {
29 #endif
30
31 // Do not directly access the fields of this struct - use the accessors only.
32 // TODO: Add a small (16 bytes, maybe?) internal buffer so we can avoid
33 // hitting the arena for short strings.
34 typedef struct {
35 size_t size_;
36 size_t capacity_;
37 char* data_;
38 upb_Arena* arena_;
39 } upb_String;
40
41 // Initialize an already-allocated upb_String object.
upb_String_Init(upb_String * s,upb_Arena * a)42 UPB_INLINE bool upb_String_Init(upb_String* s, upb_Arena* a) {
43 static const int kDefaultCapacity = 16;
44
45 s->size_ = 0;
46 s->capacity_ = kDefaultCapacity;
47 s->data_ = (char*)upb_Arena_Malloc(a, kDefaultCapacity);
48 s->arena_ = a;
49 if (!s->data_) return false;
50 s->data_[0] = '\0';
51 return true;
52 }
53
upb_String_Clear(upb_String * s)54 UPB_INLINE void upb_String_Clear(upb_String* s) {
55 s->size_ = 0;
56 s->data_[0] = '\0';
57 }
58
upb_String_Data(const upb_String * s)59 UPB_INLINE char* upb_String_Data(const upb_String* s) { return s->data_; }
60
upb_String_Size(const upb_String * s)61 UPB_INLINE size_t upb_String_Size(const upb_String* s) { return s->size_; }
62
upb_String_Empty(const upb_String * s)63 UPB_INLINE bool upb_String_Empty(const upb_String* s) { return s->size_ == 0; }
64
upb_String_Erase(upb_String * s,size_t pos,size_t len)65 UPB_INLINE void upb_String_Erase(upb_String* s, size_t pos, size_t len) {
66 if (pos >= s->size_) return;
67 char* des = s->data_ + pos;
68 if (pos + len > s->size_) len = s->size_ - pos;
69 char* src = des + len;
70 memmove(des, src, s->size_ - (src - s->data_) + 1);
71 s->size_ -= len;
72 }
73
upb_String_Reserve(upb_String * s,size_t size)74 UPB_INLINE bool upb_String_Reserve(upb_String* s, size_t size) {
75 if (s->capacity_ <= size) {
76 const size_t new_cap = size + 1;
77 s->data_ =
78 (char*)upb_Arena_Realloc(s->arena_, s->data_, s->capacity_, new_cap);
79 if (!s->data_) return false;
80 s->capacity_ = new_cap;
81 }
82 return true;
83 }
84
upb_String_Append(upb_String * s,const char * data,size_t size)85 UPB_INLINE bool upb_String_Append(upb_String* s, const char* data,
86 size_t size) {
87 if (s->capacity_ <= s->size_ + size) {
88 const size_t new_cap = 2 * (s->size_ + size) + 1;
89 if (!upb_String_Reserve(s, new_cap)) return false;
90 }
91
92 memcpy(s->data_ + s->size_, data, size);
93 s->size_ += size;
94 s->data_[s->size_] = '\0';
95 return true;
96 }
97
98 UPB_PRINTF(2, 0)
upb_String_AppendFmtV(upb_String * s,const char * fmt,va_list args)99 UPB_INLINE bool upb_String_AppendFmtV(upb_String* s, const char* fmt,
100 va_list args) {
101 size_t capacity = 1000;
102 char* buf = (char*)malloc(capacity);
103 bool out = false;
104 for (;;) {
105 const int n = _upb_vsnprintf(buf, capacity, fmt, args);
106 if (n < 0) break;
107 if (n < capacity) {
108 out = upb_String_Append(s, buf, n);
109 break;
110 }
111 capacity *= 2;
112 buf = (char*)realloc(buf, capacity);
113 }
114 free(buf);
115 return out;
116 }
117
118 UPB_PRINTF(2, 3)
upb_String_AppendFmt(upb_String * s,const char * fmt,...)119 UPB_INLINE bool upb_String_AppendFmt(upb_String* s, const char* fmt, ...) {
120 va_list args;
121 va_start(args, fmt);
122 const bool ok = upb_String_AppendFmtV(s, fmt, args);
123 va_end(args);
124 return ok;
125 }
126
upb_String_Assign(upb_String * s,const char * data,size_t size)127 UPB_INLINE bool upb_String_Assign(upb_String* s, const char* data,
128 size_t size) {
129 upb_String_Clear(s);
130 return upb_String_Append(s, data, size);
131 }
132
upb_String_Copy(upb_String * des,const upb_String * src)133 UPB_INLINE bool upb_String_Copy(upb_String* des, const upb_String* src) {
134 return upb_String_Assign(des, src->data_, src->size_);
135 }
136
upb_String_PushBack(upb_String * s,char ch)137 UPB_INLINE bool upb_String_PushBack(upb_String* s, char ch) {
138 return upb_String_Append(s, &ch, 1);
139 }
140
141 #ifdef __cplusplus
142 } /* extern "C" */
143 #endif
144
145 #include "upb/port/undef.inc"
146
147 #endif /* UPB_IO_STRING_H_ */
148