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