1 /*
2 * Copyright (c) 2009-2021, Google LLC
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of Google LLC nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 // An attempt to provide some of the C++ string functionality in C.
29 // Function names generally match those of corresponding C++ string methods.
30 // All buffers are copied so operations are relatively expensive.
31 // Internal character strings are always NULL-terminated.
32 // All bool functions return true on success, false on failure.
33
34 #ifndef UPB_IO_STRING_H_
35 #define UPB_IO_STRING_H_
36
37 #include <stdarg.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 #include "upb/mem/arena.h"
42 #include "upb/port/vsnprintf_compat.h"
43
44 // Must be last.
45 #include "upb/port/def.inc"
46
47 #ifdef __cplusplus
48 extern "C" {
49 #endif
50
51 // Do not directly access the fields of this struct - use the accessors only.
52 // TODO(salo): Add a small (16 bytes, maybe?) internal buffer so we can avoid
53 // hitting the arena for short strings.
54 typedef struct {
55 size_t size_;
56 size_t capacity_;
57 char* data_;
58 upb_Arena* arena_;
59 } upb_String;
60
61 // Initialize an already-allocted upb_String object.
upb_String_Init(upb_String * s,upb_Arena * a)62 UPB_INLINE bool upb_String_Init(upb_String* s, upb_Arena* a) {
63 static const int kDefaultCapacity = 16;
64
65 s->size_ = 0;
66 s->capacity_ = kDefaultCapacity;
67 s->data_ = (char*)upb_Arena_Malloc(a, kDefaultCapacity);
68 s->arena_ = a;
69 if (!s->data_) return false;
70 s->data_[0] = '\0';
71 return true;
72 }
73
upb_String_Clear(upb_String * s)74 UPB_INLINE void upb_String_Clear(upb_String* s) {
75 s->size_ = 0;
76 s->data_[0] = '\0';
77 }
78
upb_String_Data(const upb_String * s)79 UPB_INLINE char* upb_String_Data(const upb_String* s) { return s->data_; }
80
upb_String_Size(const upb_String * s)81 UPB_INLINE size_t upb_String_Size(const upb_String* s) { return s->size_; }
82
upb_String_Empty(const upb_String * s)83 UPB_INLINE bool upb_String_Empty(const upb_String* s) { return s->size_ == 0; }
84
upb_String_Erase(upb_String * s,size_t pos,size_t len)85 UPB_INLINE void upb_String_Erase(upb_String* s, size_t pos, size_t len) {
86 if (pos >= s->size_) return;
87 char* des = s->data_ + pos;
88 if (pos + len > s->size_) len = s->size_ - pos;
89 char* src = des + len;
90 memmove(des, src, s->size_ - (src - s->data_) + 1);
91 s->size_ -= len;
92 }
93
upb_String_Reserve(upb_String * s,size_t size)94 UPB_INLINE bool upb_String_Reserve(upb_String* s, size_t size) {
95 if (s->capacity_ <= size) {
96 const size_t new_cap = size + 1;
97 s->data_ =
98 (char*)upb_Arena_Realloc(s->arena_, s->data_, s->capacity_, new_cap);
99 if (!s->data_) return false;
100 s->capacity_ = new_cap;
101 }
102 return true;
103 }
104
upb_String_Append(upb_String * s,const char * data,size_t size)105 UPB_INLINE bool upb_String_Append(upb_String* s, const char* data,
106 size_t size) {
107 if (s->capacity_ <= s->size_ + size) {
108 const size_t new_cap = 2 * (s->size_ + size) + 1;
109 if (!upb_String_Reserve(s, new_cap)) return false;
110 }
111
112 memcpy(s->data_ + s->size_, data, size);
113 s->size_ += size;
114 s->data_[s->size_] = '\0';
115 return true;
116 }
117
118 UPB_PRINTF(2, 0)
upb_String_AppendFmtV(upb_String * s,const char * fmt,va_list args)119 UPB_INLINE bool upb_String_AppendFmtV(upb_String* s, const char* fmt,
120 va_list args) {
121 size_t capacity = 1000;
122 char* buf = (char*)malloc(capacity);
123 bool out = false;
124 for (;;) {
125 const int n = _upb_vsnprintf(buf, capacity, fmt, args);
126 if (n < 0) break;
127 if (n < capacity) {
128 out = upb_String_Append(s, buf, n);
129 break;
130 }
131 capacity *= 2;
132 buf = (char*)realloc(buf, capacity);
133 }
134 free(buf);
135 return out;
136 }
137
138 UPB_PRINTF(2, 3)
upb_String_AppendFmt(upb_String * s,const char * fmt,...)139 UPB_INLINE bool upb_String_AppendFmt(upb_String* s, const char* fmt, ...) {
140 va_list args;
141 va_start(args, fmt);
142 const bool ok = upb_String_AppendFmtV(s, fmt, args);
143 va_end(args);
144 return ok;
145 }
146
upb_String_Assign(upb_String * s,const char * data,size_t size)147 UPB_INLINE bool upb_String_Assign(upb_String* s, const char* data,
148 size_t size) {
149 upb_String_Clear(s);
150 return upb_String_Append(s, data, size);
151 }
152
upb_String_Copy(upb_String * des,const upb_String * src)153 UPB_INLINE bool upb_String_Copy(upb_String* des, const upb_String* src) {
154 return upb_String_Assign(des, src->data_, src->size_);
155 }
156
upb_String_PushBack(upb_String * s,char ch)157 UPB_INLINE bool upb_String_PushBack(upb_String* s, char ch) {
158 return upb_String_Append(s, &ch, 1);
159 }
160
161 #ifdef __cplusplus
162 } /* extern "C" */
163 #endif
164
165 #include "upb/port/undef.inc"
166
167 #endif /* UPB_IO_STRING_H_ */
168