Split upb::Arena/upb::Allocator from upb::Environment. (#58)

* Split upb::Arena/upb::Allocator from upb::Environment.

This will allow arenas and allocators to be used
independently of environments, which will be important
for an upcoming change (a message representation).
Overall this design feels cleaner that the previous
Environment/SeededAllocator design.

As part of this change, moved all allocations in upb
to use a global allocator instead of hard-coding
malloc/free.  This will allow injecting OOM faults
for more robust testing.

One place that doesn't use the global allocator is
the tracked ref code.  Instead of its previous approach
of CHECK_OOM() after every malloc() or table insert, it
simply uses an allocator that does this automatically.

I moved Allocator/Arena/Environment into upb.h.
This seems principled since these are the only types
in upb whose size is directly exposed to users, since
they form the basis of memory allocation strategy.

* Cleaned up some header includes and fixed more malloc -> upb_gmalloc().

* Changes from PR review.

* Don't use UINTPTR_MAX or UINT64_MAX.

* Punt on adding line/file for now.

* We actually can't store (uint64_t)-1, update comment and test.
pull/13171/head
Joshua Haberman 9 years ago
parent 04786dc2b3
commit 68bc62a7fa
  1. 1
      Makefile
  2. 5
      tests/pb/test_encoder.cc
  3. 1
      tests/test_cpp.cc
  4. 9
      tests/test_def.c
  5. 418
      tests/test_table.cc
  6. 1
      tests/test_util.h
  7. 2
      travis.sh
  8. 153
      upb/def.c
  9. 59
      upb/descriptor/reader.c
  10. 1
      upb/descriptor/reader.h
  11. 273
      upb/env.c
  12. 262
      upb/env.h
  13. 22
      upb/handlers.c
  14. 91
      upb/json/parser.c
  15. 3
      upb/json/parser.h
  16. 19
      upb/json/parser.rl
  17. 16
      upb/json/printer.c
  18. 3
      upb/json/printer.h
  19. 17
      upb/pb/compile_decoder.c
  20. 3
      upb/pb/decoder.h
  21. 1
      upb/pb/decoder.int.h
  22. 5
      upb/pb/encoder.c
  23. 1
      upb/pb/encoder.h
  24. 5
      upb/pb/glue.c
  25. 3
      upb/pb/glue.h
  26. 5
      upb/pb/textprinter.c
  27. 1
      upb/pb/textprinter.h
  28. 105
      upb/refcounted.c
  29. 111
      upb/refcounted.h
  30. 6
      upb/shim/shim.c
  31. 25
      upb/symtab.c
  32. 135
      upb/table.c
  33. 109
      upb/table.int.h
  34. 260
      upb/upb.c
  35. 544
      upb/upb.h

@ -148,7 +148,6 @@ make_objs_cc = $$(patsubst upb/$$(pc).cc,obj/upb/$$(pc).$(1),$$($$(call to_srcs,
upb_SRCS = \
upb/def.c \
upb/env.c \
upb/handlers.c \
upb/refcounted.c \
upb/shim/shim.c \

@ -26,10 +26,7 @@ void test_pb_roundtrip() {
upb::pb::DecoderMethod::New(
upb::pb::DecoderMethodOptions(encoder_handlers.get())));
char buf[512];
upb::SeededAllocator alloc(buf, sizeof(buf));
upb::Environment env;
env.SetAllocator(&alloc);
upb::InlinedEnvironment<512> env;
std::string input = read_string("upb/descriptor/descriptor.pb");
std::string output;
upb::StringSink string_sink(&output);

@ -12,7 +12,6 @@
#include <sstream>
#include "upb/def.h"
#include "upb/env.h"
#include "upb/descriptor/reader.h"
#include "upb/handlers.h"
#include "upb/pb/decoder.h"

@ -44,20 +44,23 @@ static upb_symtab *load_test_proto(void *owner) {
upb_status status = UPB_STATUS_INIT;
size_t len;
char *data = upb_readfile(descriptor_file, &len);
upb_filedef **files;
upb_filedef **files, **files_ptr;
ASSERT(s);
ASSERT(data);
files = upb_loaddescriptor(data, len, &files, &status);
ASSERT(files);
free(data);
while (*files) {
files_ptr = files;
while (*files_ptr) {
bool ok = upb_symtab_addfile(s, *files, &status);
ASSERT(ok);
upb_filedef_unref(*files, &files);
files++;
files_ptr++;
}
upb_gfree(files);
ASSERT(!upb_symtab_isfrozen(s));
upb_symtab_freeze(s);
ASSERT(upb_symtab_isfrozen(s));

@ -15,6 +15,318 @@
#include "tests/upb_test.h"
#include "upb/table.int.h"
// Convenience interface for C++. We don't put this in upb itself because
// the table is not exposed to users.
namespace upb {
template <class T> upb_value MakeUpbValue(T val);
template <class T> T GetUpbValue(upb_value val);
template <class T> upb_ctype_t GetUpbValueType();
#define FUNCS(name, type_t, enumval) \
template<> upb_value MakeUpbValue<type_t>(type_t val) { return upb_value_ ## name(val); } \
template<> type_t GetUpbValue<type_t>(upb_value val) { return upb_value_get ## name(val); } \
template<> upb_ctype_t GetUpbValueType<type_t>() { return enumval; }
FUNCS(int32, int32_t, UPB_CTYPE_INT32)
FUNCS(int64, int64_t, UPB_CTYPE_INT64)
FUNCS(uint32, uint32_t, UPB_CTYPE_UINT32)
FUNCS(uint64, uint64_t, UPB_CTYPE_UINT64)
FUNCS(bool, bool, UPB_CTYPE_BOOL)
FUNCS(cstr, char*, UPB_CTYPE_CSTR)
FUNCS(ptr, void*, UPB_CTYPE_PTR)
FUNCS(constptr, const void*, UPB_CTYPE_CONSTPTR)
FUNCS(fptr, upb_func*, UPB_CTYPE_FPTR)
#undef FUNCS
class IntTable {
public:
IntTable(upb_ctype_t value_type) { upb_inttable_init(&table_, value_type); }
~IntTable() { upb_inttable_uninit(&table_); }
size_t count() { return upb_inttable_count(&table_); }
bool Insert(uintptr_t key, upb_value val) {
return upb_inttable_insert(&table_, key, val);
}
bool Replace(uintptr_t key, upb_value val) {
return upb_inttable_replace(&table_, key, val);
}
std::pair<bool, upb_value> Remove(uintptr_t key) {
std::pair<bool, upb_value> ret;
ret.first = upb_inttable_remove(&table_, key, &ret.second);
return ret;
}
std::pair<bool, upb_value> Lookup(uintptr_t key) const {
std::pair<bool, upb_value> ret;
ret.first = upb_inttable_lookup(&table_, key, &ret.second);
return ret;
}
std::pair<bool, upb_value> Lookup32(uint32_t key) const {
std::pair<bool, upb_value> ret;
ret.first = upb_inttable_lookup32(&table_, key, &ret.second);
return ret;
}
void Compact() { upb_inttable_compact(&table_); }
class iterator : public std::iterator<std::forward_iterator_tag,
std::pair<uintptr_t, upb_value> > {
public:
explicit iterator(IntTable* table) {
upb_inttable_begin(&iter_, &table->table_);
}
static iterator end(IntTable* table) {
iterator iter(table);
upb_inttable_iter_setdone(&iter.iter_);
return iter;
}
void operator++() {
return upb_inttable_next(&iter_);
}
std::pair<uintptr_t, upb_value> operator*() const {
std::pair<uintptr_t, upb_value> ret;
ret.first = upb_inttable_iter_key(&iter_);
ret.second = upb_inttable_iter_value(&iter_);
return ret;
}
bool operator==(const iterator& other) const {
return upb_inttable_iter_isequal(&iter_, &other.iter_);
}
bool operator!=(const iterator& other) const {
return !(*this == other);
}
private:
upb_inttable_iter iter_;
};
upb_inttable table_;
};
class StrTable {
public:
StrTable(upb_ctype_t value_type) { upb_strtable_init(&table_, value_type); }
~StrTable() { upb_strtable_uninit(&table_); }
size_t count() { return upb_strtable_count(&table_); }
bool Insert(const std::string& key, upb_value val) {
return upb_strtable_insert2(&table_, key.c_str(), key.size(), val);
}
std::pair<bool, upb_value> Remove(const std::string& key) {
std::pair<bool, upb_value> ret;
ret.first =
upb_strtable_remove2(&table_, key.c_str(), key.size(), &ret.second);
return ret;
}
std::pair<bool, upb_value> Lookup(const std::string& key) const {
std::pair<bool, upb_value> ret;
ret.first =
upb_strtable_lookup2(&table_, key.c_str(), key.size(), &ret.second);
return ret;
}
void Resize(size_t size_lg2) {
upb_strtable_resize(&table_, size_lg2, &upb_alloc_global);
}
class iterator : public std::iterator<std::forward_iterator_tag,
std::pair<std::string, upb_value> > {
public:
explicit iterator(StrTable* table) {
upb_strtable_begin(&iter_, &table->table_);
}
static iterator end(StrTable* table) {
iterator iter(table);
upb_strtable_iter_setdone(&iter.iter_);
return iter;
}
void operator++() {
return upb_strtable_next(&iter_);
}
std::pair<std::string, upb_value> operator*() const {
std::pair<std::string, upb_value> ret;
ret.first.assign(upb_strtable_iter_key(&iter_));
ret.second = upb_strtable_iter_value(&iter_);
return ret;
}
bool operator==(const iterator& other) const {
return upb_strtable_iter_isequal(&iter_, &other.iter_);
}
bool operator!=(const iterator& other) const {
return !(*this == other);
}
private:
upb_strtable_iter iter_;
};
upb_strtable table_;
};
template <class T> class TypedStrTable {
public:
TypedStrTable() : table_(GetUpbValueType<T>()) {}
size_t count() { return table_.count(); }
bool Insert(const std::string &key, T val) {
return table_.Insert(key, MakeUpbValue<T>(val));
}
std::pair<bool, T> Remove(const std::string& key) {
std::pair<bool, upb_value> found = table_.Remove(key);
std::pair<bool, T> ret;
ret.first = found.first;
if (ret.first) {
ret.second = GetUpbValue<T>(found.second);
}
return ret;
}
std::pair<bool, T> Lookup(const std::string& key) const {
std::pair<bool, upb_value> found = table_.Lookup(key);
std::pair<bool, T> ret;
ret.first = found.first;
if (ret.first) {
ret.second = GetUpbValue<T>(found.second);
}
return ret;
}
void Resize(size_t size_lg2) {
table_.Resize(size_lg2);
}
class iterator : public std::iterator<std::forward_iterator_tag, std::pair<std::string, T> > {
public:
explicit iterator(TypedStrTable* table) : iter_(&table->table_) {}
static iterator end(TypedStrTable* table) {
iterator iter(table);
iter.iter_ = StrTable::iterator::end(&table->table_);
return iter;
}
void operator++() { ++iter_; }
std::pair<std::string, T> operator*() const {
std::pair<std::string, upb_value> val = *iter_;
std::pair<std::string, T> ret;
ret.first = val.first;
ret.second = GetUpbValue<T>(val.second);
return ret;
}
bool operator==(const iterator& other) const {
return iter_ == other.iter_;
}
bool operator!=(const iterator& other) const {
return iter_ != other.iter_;
}
private:
StrTable::iterator iter_;
};
iterator begin() { return iterator(this); }
iterator end() { return iterator::end(this); }
StrTable table_;
};
template <class T> class TypedIntTable {
public:
TypedIntTable() : table_(GetUpbValueType<T>()) {}
size_t count() { return table_.count(); }
bool Insert(uintptr_t key, T val) {
return table_.Insert(key, MakeUpbValue<T>(val));
}
bool Replace(uintptr_t key, T val) {
return table_.Replace(key, MakeUpbValue<T>(val));
}
std::pair<bool, T> Remove(uintptr_t key) {
std::pair<bool, upb_value> found = table_.Remove(key);
std::pair<bool, T> ret;
ret.first = found.first;
if (ret.first) {
ret.second = GetUpbValue<T>(found.second);
}
return ret;
}
std::pair<bool, T> Lookup(uintptr_t key) const {
std::pair<bool, upb_value> found = table_.Lookup(key);
std::pair<bool, T> ret;
ret.first = found.first;
if (ret.first) {
ret.second = GetUpbValue<T>(found.second);
}
return ret;
}
void Compact() { table_.Compact(); }
class iterator : public std::iterator<std::forward_iterator_tag, std::pair<uintptr_t, T> > {
public:
explicit iterator(TypedIntTable* table) : iter_(&table->table_) {}
static iterator end(TypedIntTable* table) {
return IntTable::iterator::end(&table->table_);
}
void operator++() { ++iter_; }
std::pair<uintptr_t, T> operator*() const {
std::pair<uintptr_t, upb_value> val = *iter_;
std::pair<uintptr_t, T> ret;
ret.first = val.first;
ret.second = GetUpbValue<T>(val.second);
return ret;
}
bool operator==(const iterator& other) const {
return iter_ == other.iter_;
}
bool operator!=(const iterator& other) const {
return iter_ != other.iter_;
}
private:
IntTable::iterator iter_;
};
iterator begin() { return iterator(this); }
iterator end() { return iterator::end(this); }
IntTable table_;
};
}
bool benchmark = false;
#define CPU_TIME_PER_TEST 0.5
@ -29,38 +341,32 @@ double get_usertime() {
/* num_entries must be a power of 2. */
void test_strtable(const vector<std::string>& keys, uint32_t num_to_insert) {
/* Initialize structures. */
upb_strtable table;
std::map<std::string, int32_t> m;
upb_strtable_init(&table, UPB_CTYPE_INT32);
typedef upb::TypedStrTable<int32_t> Table;
Table table;
std::set<std::string> all;
for(size_t i = 0; i < num_to_insert; i++) {
const std::string& key = keys[i];
all.insert(key);
upb_strtable_insert(&table, key.c_str(), upb_value_int32(key[0]));
table.Insert(key, key[0]);
m[key] = key[0];
}
/* Test correctness. */
for(uint32_t i = 0; i < keys.size(); i++) {
const std::string& key = keys[i];
upb_value v;
bool found = upb_strtable_lookup(&table, key.c_str(), &v);
std::pair<bool, int32_t> found = table.Lookup(key);
if(m.find(key) != m.end()) { /* Assume map implementation is correct. */
ASSERT(found);
ASSERT(upb_value_getint32(v) == key[0]);
ASSERT(found.first);
ASSERT(found.second == key[0]);
ASSERT(m[key] == key[0]);
} else {
ASSERT(!found);
ASSERT(!found.first);
}
}
upb_strtable_iter iter;
for(upb_strtable_begin(&iter, &table); !upb_strtable_done(&iter);
upb_strtable_next(&iter)) {
const char *key = upb_strtable_iter_key(&iter);
std::string tmp(key, strlen(key));
ASSERT(strlen(key) == upb_strtable_iter_keylength(&iter));
std::set<std::string>::iterator i = all.find(tmp);
for (Table::iterator it = table.begin(); it != table.end(); ++it) {
std::set<std::string>::iterator i = all.find((*it).first);
ASSERT(i != all.end());
all.erase(i);
}
@ -69,84 +375,76 @@ void test_strtable(const vector<std::string>& keys, uint32_t num_to_insert) {
// Test iteration with resizes.
for (int i = 0; i < 10; i++) {
for(upb_strtable_begin(&iter, &table); !upb_strtable_done(&iter);
upb_strtable_next(&iter)) {
for (Table::iterator it = table.begin(); it != table.end(); ++it) {
// Even if we invalidate the iterator it should only return real elements.
const char *key = upb_strtable_iter_key(&iter);
std::string tmp(key, strlen(key));
ASSERT(upb_value_getint32(upb_strtable_iter_value(&iter)) == m[tmp]);
ASSERT((*it).second == m[(*it).first]);
// Force a resize even though the size isn't changing.
// Also forces the table size to grow so some new buckets end up empty.
int new_lg2 = table.t.size_lg2 + 1;
int new_lg2 = table.table_.table_.t.size_lg2 + 1;
// Don't use more than 64k tables, to avoid exhausting memory.
new_lg2 = UPB_MIN(new_lg2, 16);
upb_strtable_resize(&table, new_lg2);
table.Resize(new_lg2);
}
}
upb_strtable_uninit(&table);
}
/* num_entries must be a power of 2. */
void test_inttable(int32_t *keys, uint16_t num_entries, const char *desc) {
/* Initialize structures. */
upb_inttable table;
typedef upb::TypedIntTable<uint32_t> Table;
Table table;
uint32_t largest_key = 0;
std::map<uint32_t, uint32_t> m;
__gnu_cxx::hash_map<uint32_t, uint32_t> hm;
upb_inttable_init(&table, UPB_CTYPE_UINT32);
for(size_t i = 0; i < num_entries; i++) {
int32_t key = keys[i];
largest_key = UPB_MAX((int32_t)largest_key, key);
upb_inttable_insert(&table, key, upb_value_uint32(key * 2));
table.Insert(key, key * 2);
m[key] = key*2;
hm[key] = key*2;
}
/* Test correctness. */
for(uint32_t i = 0; i <= largest_key; i++) {
upb_value v;
bool found = upb_inttable_lookup(&table, i, &v);
std::pair<bool, uint32_t> found = table.Lookup(i);
if(m.find(i) != m.end()) { /* Assume map implementation is correct. */
ASSERT(found);
ASSERT(upb_value_getuint32(v) == i*2);
ASSERT(found.first);
ASSERT(found.second == i*2);
ASSERT(m[i] == i*2);
ASSERT(hm[i] == i*2);
} else {
ASSERT(!found);
ASSERT(!found.first);
}
}
for(uint16_t i = 0; i < num_entries; i += 2) {
upb_value val;
bool ret = upb_inttable_remove(&table, keys[i], &val);
ASSERT(ret == (m.erase(keys[i]) == 1));
if (ret) ASSERT(upb_value_getuint32(val) == (uint32_t)keys[i] * 2);
std::pair<bool, uint32_t> found = table.Remove(keys[i]);
ASSERT(found.first == (m.erase(keys[i]) == 1));
if (found.first) ASSERT(found.second == (uint32_t)keys[i] * 2);
hm.erase(keys[i]);
m.erase(keys[i]);
}
ASSERT(upb_inttable_count(&table) == hm.size());
ASSERT(table.count() == hm.size());
/* Test correctness. */
for(uint32_t i = 0; i <= largest_key; i++) {
upb_value v;
bool found = upb_inttable_lookup(&table, i, &v);
std::pair<bool, uint32_t> found = table.Lookup(i);
if(m.find(i) != m.end()) { /* Assume map implementation is correct. */
ASSERT(found);
ASSERT(upb_value_getuint32(v) == i*2);
ASSERT(found.first);
ASSERT(found.second == i*2);
ASSERT(m[i] == i*2);
ASSERT(hm[i] == i*2);
} else {
ASSERT(!found);
ASSERT(!found.first);
}
}
// Test replace.
for(uint32_t i = 0; i <= largest_key; i++) {
upb_value v = upb_value_uint32(i*3);
bool replaced = upb_inttable_replace(&table, i, v);
bool replaced = table.Replace(i, i*3);
if(m.find(i) != m.end()) { /* Assume map implementation is correct. */
ASSERT(replaced);
m[i] = i * 3;
@ -157,22 +455,20 @@ void test_inttable(int32_t *keys, uint16_t num_entries, const char *desc) {
}
// Compact and test correctness again.
upb_inttable_compact(&table);
table.Compact();
for(uint32_t i = 0; i <= largest_key; i++) {
upb_value v;
bool found = upb_inttable_lookup(&table, i, &v);
std::pair<bool, uint32_t> found = table.Lookup(i);
if(m.find(i) != m.end()) { /* Assume map implementation is correct. */
ASSERT(found);
ASSERT(upb_value_getuint32(v) == i*3);
ASSERT(found.first);
ASSERT(found.second == i*3);
ASSERT(m[i] == i*3);
ASSERT(hm[i] == i*3);
} else {
ASSERT(!found);
ASSERT(!found.first);
}
}
if(!benchmark) {
upb_inttable_uninit(&table);
return;
}
@ -207,7 +503,7 @@ void test_inttable(int32_t *keys, uint16_t num_entries, const char *desc) {
MAYBE_BREAK;
int32_t key = keys[i & mask];
upb_value v;
bool ok = upb_inttable_lookup32(&table, key, &v);
bool ok = upb_inttable_lookup32(&table.table_.table_, key, &v);
x += (uintptr_t)ok;
}
double total = get_usertime() - before;
@ -221,7 +517,7 @@ void test_inttable(int32_t *keys, uint16_t num_entries, const char *desc) {
MAYBE_BREAK;
int32_t key = keys[rand_order[i & mask]];
upb_value v;
bool ok = upb_inttable_lookup32(&table, key, &v);
bool ok = upb_inttable_lookup32(&table.table_.table_, key, &v);
x += (uintptr_t)ok;
}
total = get_usertime() - before;
@ -272,10 +568,25 @@ void test_inttable(int32_t *keys, uint16_t num_entries, const char *desc) {
total = get_usertime() - before;
if (x == INT_MAX) abort();
printf("%ld/s (%0.1f%% of upb)\n\n", (long)(i/total), i / upb_rand_i);
upb_inttable_uninit(&table);
delete[] rand_order;
}
/*
* This test can't pass right now because the table can't store a value of
* (uint64_t)-1.
*/
void test_int64_max_value() {
/*
typedef upb::TypedIntTable<uint64_t> Table;
Table table;
uintptr_t uint64_max = (uint64_t)-1;
table.Insert(1, uint64_max);
std::pair<bool, uint64_t> found = table.Lookup(1);
ASSERT(found.first);
ASSERT(found.second == uint64_max);
*/
}
int32_t *get_contiguous_keys(int32_t num) {
int32_t *buf = new int32_t[num];
for(int32_t i = 0; i < num; i++)
@ -357,6 +668,7 @@ int run_tests(int argc, char *argv[]) {
delete[] keys4;
test_delete();
test_int64_max_value();
return 0;
}

@ -8,7 +8,6 @@
#include <stdio.h>
#include <math.h>
#include "tests/upb_test.h"
#include "upb/env.h"
#include "upb/sink.h"
#ifdef __cplusplus

@ -166,7 +166,7 @@ if [ "$CC" != "gcc" ] && [ "$UPB_TRAVIS_BUILD" == "coverage" ]; then
fi
# Enable asserts and ref debugging (though some configurations override this).
export USER_CPPFLAGS="-UNDEBUG -DUPB_DEBUG_REFS -DUPB_THREAD_UNSAFE -g"
export USER_CPPFLAGS="-UNDEBUG -DUPB_DEBUG_REFS -DUPB_THREAD_UNSAFE -DUPB_DEBUG_TABLE -g"
if [ "$CC" == "gcc" ]; then
# For the GCC build test loading JIT code via SO. For the Clang build test

@ -13,7 +13,7 @@ typedef struct {
} str_t;
static str_t *newstr(const char *data, size_t len) {
str_t *ret = malloc(sizeof(*ret) + len);
str_t *ret = upb_gmalloc(sizeof(*ret) + len);
if (!ret) return NULL;
ret->len = len;
memcpy(ret->str, data, len);
@ -21,7 +21,7 @@ static str_t *newstr(const char *data, size_t len) {
return ret;
}
static void freestr(str_t *s) { free(s); }
static void freestr(str_t *s) { upb_gfree(s); }
/* isalpha() etc. from <ctype.h> are locale-dependent, which we don't want. */
static bool upb_isbetween(char c, char low, char high) {
@ -89,9 +89,18 @@ const char *upb_def_name(const upb_def *d) {
bool upb_def_setfullname(upb_def *def, const char *fullname, upb_status *s) {
assert(!upb_def_isfrozen(def));
if (!upb_isident(fullname, strlen(fullname), true, s)) return false;
free((void*)def->fullname);
def->fullname = upb_strdup(fullname);
if (!upb_isident(fullname, strlen(fullname), true, s)) {
return false;
}
fullname = upb_gstrdup(fullname);
if (!fullname) {
upb_upberr_setoom(s);
return false;
}
upb_gfree((void*)def->fullname);
def->fullname = fullname;
return true;
}
@ -124,7 +133,7 @@ static bool upb_def_init(upb_def *def, upb_deftype_t type,
}
static void upb_def_uninit(upb_def *def) {
free((void*)def->fullname);
upb_gfree((void*)def->fullname);
}
static const char *msgdef_name(const upb_msgdef *m) {
@ -261,8 +270,19 @@ static bool assign_msg_indices(upb_msgdef *m, upb_status *s) {
int i;
uint32_t selector;
int n = upb_msgdef_numfields(m);
upb_fielddef **fields = malloc(n * sizeof(*fields));
if (!fields) return false;
upb_fielddef **fields;
if (n == 0) {
m->selector_count = UPB_STATIC_SELECTOR_COUNT;
m->submsg_field_count = 0;
return true;
}
fields = upb_gmalloc(n * sizeof(*fields));
if (!fields) {
upb_upberr_setoom(s);
return false;
}
m->submsg_field_count = 0;
for(i = 0, upb_msg_field_begin(&j, m);
@ -271,7 +291,7 @@ static bool assign_msg_indices(upb_msgdef *m, upb_status *s) {
upb_fielddef *f = upb_msg_iter_field(&j);
assert(f->msg.def == m);
if (!upb_validate_field(f, s)) {
free(fields);
upb_gfree(fields);
return false;
}
if (upb_fielddef_issubmsg(f)) {
@ -331,7 +351,7 @@ static bool assign_msg_indices(upb_msgdef *m, upb_status *s) {
#undef TRY
#endif
free(fields);
upb_gfree(fields);
return true;
}
@ -408,18 +428,18 @@ static void upb_enumdef_free(upb_refcounted *r) {
upb_inttable_iter i;
upb_inttable_begin(&i, &e->iton);
for( ; !upb_inttable_done(&i); upb_inttable_next(&i)) {
/* To clean up the upb_strdup() from upb_enumdef_addval(). */
free(upb_value_getcstr(upb_inttable_iter_value(&i)));
/* To clean up the upb_gstrdup() from upb_enumdef_addval(). */
upb_gfree(upb_value_getcstr(upb_inttable_iter_value(&i)));
}
upb_strtable_uninit(&e->ntoi);
upb_inttable_uninit(&e->iton);
upb_def_uninit(upb_enumdef_upcast_mutable(e));
free(e);
upb_gfree(e);
}
upb_enumdef *upb_enumdef_new(const void *owner) {
static const struct upb_refcounted_vtbl vtbl = {NULL, &upb_enumdef_free};
upb_enumdef *e = malloc(sizeof(*e));
upb_enumdef *e = upb_gmalloc(sizeof(*e));
if (!e) return NULL;
if (!upb_def_init(upb_enumdef_upcast_mutable(e), UPB_DEF_ENUM, &vtbl, owner))
goto err2;
@ -430,7 +450,7 @@ upb_enumdef *upb_enumdef_new(const void *owner) {
err1:
upb_strtable_uninit(&e->ntoi);
err2:
free(e);
upb_gfree(e);
return NULL;
}
@ -469,27 +489,36 @@ bool upb_enumdef_setfullname(upb_enumdef *e, const char *fullname,
bool upb_enumdef_addval(upb_enumdef *e, const char *name, int32_t num,
upb_status *status) {
char *name2;
if (!upb_isident(name, strlen(name), false, status)) {
return false;
}
if (upb_enumdef_ntoiz(e, name, NULL)) {
upb_status_seterrf(status, "name '%s' is already defined", name);
return false;
}
if (!upb_strtable_insert(&e->ntoi, name, upb_value_int32(num))) {
upb_status_seterrmsg(status, "out of memory");
return false;
}
if (!upb_inttable_lookup(&e->iton, num, NULL) &&
!upb_inttable_insert(&e->iton, num, upb_value_cstr(upb_strdup(name)))) {
if (!upb_inttable_lookup(&e->iton, num, NULL)) {
name2 = upb_gstrdup(name);
if (!name2 || !upb_inttable_insert(&e->iton, num, upb_value_cstr(name2))) {
upb_status_seterrmsg(status, "out of memory");
upb_strtable_remove(&e->ntoi, name, NULL);
return false;
}
}
if (upb_enumdef_numvals(e) == 1) {
bool ok = upb_enumdef_setdefault(e, num, NULL);
UPB_ASSERT_VAR(ok, ok);
}
return true;
}
@ -576,9 +605,9 @@ static void freefield(upb_refcounted *r) {
upb_fielddef *f = (upb_fielddef*)r;
upb_fielddef_uninit_default(f);
if (f->subdef_is_symbolic)
free(f->sub.name);
upb_gfree(f->sub.name);
upb_def_uninit(upb_fielddef_upcast_mutable(f));
free(f);
upb_gfree(f);
}
static const char *enumdefaultstr(const upb_fielddef *f) {
@ -636,10 +665,10 @@ static bool enumdefaultint32(const upb_fielddef *f, int32_t *val) {
upb_fielddef *upb_fielddef_new(const void *o) {
static const struct upb_refcounted_vtbl vtbl = {visitfield, freefield};
upb_fielddef *f = malloc(sizeof(*f));
upb_fielddef *f = upb_gmalloc(sizeof(*f));
if (!f) return NULL;
if (!upb_def_init(upb_fielddef_upcast_mutable(f), UPB_DEF_FIELD, &vtbl, o)) {
free(f);
upb_gfree(f);
return NULL;
}
f->msg.def = NULL;
@ -690,7 +719,7 @@ upb_fielddef *upb_fielddef_dup(const upb_fielddef *f, const void *owner) {
srcname = f->sub.def ? upb_def_fullname(f->sub.def) : NULL;
}
if (srcname) {
char *newname = malloc(strlen(f->sub.def->fullname) + 2);
char *newname = upb_gmalloc(strlen(f->sub.def->fullname) + 2);
if (!newname) {
upb_fielddef_unref(newf, owner);
return NULL;
@ -698,7 +727,7 @@ upb_fielddef *upb_fielddef_dup(const upb_fielddef *f, const void *owner) {
strcpy(newname, ".");
strcat(newname, f->sub.def->fullname);
upb_fielddef_setsubdefname(newf, newname, NULL);
free(newname);
upb_gfree(newname);
}
return newf;
@ -805,11 +834,12 @@ const char *upb_fielddef_containingtypename(upb_fielddef *f) {
}
static void release_containingtype(upb_fielddef *f) {
if (f->msg_is_symbolic) free(f->msg.name);
if (f->msg_is_symbolic) upb_gfree(f->msg.name);
}
bool upb_fielddef_setcontainingtypename(upb_fielddef *f, const char *name,
upb_status *s) {
char *name_copy;
assert(!upb_fielddef_isfrozen(f));
if (upb_fielddef_containingtype(f)) {
upb_status_seterrmsg(s, "field has already been added to a message.");
@ -817,8 +847,15 @@ bool upb_fielddef_setcontainingtypename(upb_fielddef *f, const char *name,
}
/* TODO: validate name (upb_isident() doesn't quite work atm because this name
* may have a leading "."). */
name_copy = upb_gstrdup(name);
if (!name_copy) {
upb_upberr_setoom(s);
return false;
}
release_containingtype(f);
f->msg.name = upb_strdup(name);
f->msg.name = name_copy;
f->msg_is_symbolic = true;
return true;
}
@ -1219,7 +1256,7 @@ static bool upb_subdef_typecheck(upb_fielddef *f, const upb_def *subdef,
static void release_subdef(upb_fielddef *f) {
if (f->subdef_is_symbolic) {
free(f->sub.name);
upb_gfree(f->sub.name);
} else if (f->sub.def) {
upb_unref2(f->sub.def, f);
}
@ -1249,15 +1286,23 @@ bool upb_fielddef_setenumsubdef(upb_fielddef *f, const upb_enumdef *subdef,
bool upb_fielddef_setsubdefname(upb_fielddef *f, const char *name,
upb_status *s) {
char *name_copy;
assert(!upb_fielddef_isfrozen(f));
if (!upb_fielddef_hassubdef(f)) {
upb_status_seterrmsg(s, "field type does not accept a subdef");
return false;
}
name_copy = upb_gstrdup(name);
if (!name_copy) {
upb_upberr_setoom(s);
return false;
}
/* TODO: validate name (upb_isident() doesn't quite work atm because this name
* may have a leading "."). */
release_subdef(f);
f->sub.name = upb_strdup(name);
f->sub.name = name_copy;
f->subdef_is_symbolic = true;
return true;
}
@ -1337,12 +1382,12 @@ static void freemsg(upb_refcounted *r) {
upb_strtable_uninit(&m->ntof);
upb_inttable_uninit(&m->itof);
upb_def_uninit(upb_msgdef_upcast_mutable(m));
free(m);
upb_gfree(m);
}
upb_msgdef *upb_msgdef_new(const void *owner) {
static const struct upb_refcounted_vtbl vtbl = {visitmsg, freemsg};
upb_msgdef *m = malloc(sizeof(*m));
upb_msgdef *m = upb_gmalloc(sizeof(*m));
if (!m) return NULL;
if (!upb_def_init(upb_msgdef_upcast_mutable(m), UPB_DEF_MSG, &vtbl, owner))
goto err2;
@ -1358,7 +1403,7 @@ err1:
err2:
upb_inttable_uninit(&m->itof);
err3:
free(m);
upb_gfree(m);
return NULL;
}
@ -1614,13 +1659,13 @@ static void freeoneof(upb_refcounted *r) {
upb_oneofdef *o = (upb_oneofdef*)r;
upb_strtable_uninit(&o->ntof);
upb_inttable_uninit(&o->itof);
free((void*)o->name);
free(o);
upb_gfree((void*)o->name);
upb_gfree(o);
}
upb_oneofdef *upb_oneofdef_new(const void *owner) {
static const struct upb_refcounted_vtbl vtbl = {visitoneof, freeoneof};
upb_oneofdef *o = malloc(sizeof(*o));
upb_oneofdef *o = upb_gmalloc(sizeof(*o));
o->parent = NULL;
if (!o) return NULL;
if (!upb_refcounted_init(upb_oneofdef_upcast_mutable(o), &vtbl, owner))
@ -1633,7 +1678,7 @@ upb_oneofdef *upb_oneofdef_new(const void *owner) {
err1:
upb_inttable_uninit(&o->itof);
err2:
free(o);
upb_gfree(o);
return NULL;
}
@ -1662,9 +1707,19 @@ bool upb_oneofdef_setname(upb_oneofdef *o, const char *name, upb_status *s) {
upb_status_seterrmsg(s, "oneof already added to a message");
return false;
}
if (!upb_isident(name, strlen(name), true, s)) return false;
free((void*)o->name);
o->name = upb_strdup(name);
if (!upb_isident(name, strlen(name), true, s)) {
return false;
}
name = upb_gstrdup(name);
if (!name) {
upb_status_seterrmsg(s, "One of memory");
return false;
}
upb_gfree((void*)o->name);
o->name = name;
return true;
}
@ -1806,14 +1861,14 @@ static void freefiledef(upb_refcounted *r) {
upb_inttable_uninit(&f->defs);
upb_inttable_uninit(&f->deps);
free((void*)f->name);
free((void*)f->package);
free(f);
upb_gfree((void*)f->name);
upb_gfree((void*)f->package);
upb_gfree(f);
}
upb_filedef *upb_filedef_new(const void *owner) {
static const struct upb_refcounted_vtbl vtbl = {visitfiledef, freefiledef};
upb_filedef *f = malloc(sizeof(*f));
upb_filedef *f = upb_gmalloc(sizeof(*f));
if (!f) {
return NULL;
@ -1842,7 +1897,7 @@ err2:
upb_inttable_uninit(&f->defs);
err:
free(f);
upb_gfree(f);
return NULL;
}
@ -1887,12 +1942,12 @@ const upb_filedef *upb_filedef_dep(const upb_filedef *f, size_t i) {
}
bool upb_filedef_setname(upb_filedef *f, const char *name, upb_status *s) {
name = upb_strdup(name);
name = upb_gstrdup(name);
if (!name) {
upb_status_seterrmsg(s, "Out of memory");
upb_upberr_setoom(s);
return false;
}
free((void*)f->name);
upb_gfree((void*)f->name);
f->name = name;
return true;
}
@ -1900,12 +1955,12 @@ bool upb_filedef_setname(upb_filedef *f, const char *name, upb_status *s) {
bool upb_filedef_setpackage(upb_filedef *f, const char *package,
upb_status *s) {
if (!upb_isident(package, strlen(package), true, s)) return false;
package = upb_strdup(package);
package = upb_gstrdup(package);
if (!package) {
upb_status_seterrmsg(s, "Out of memory");
upb_upberr_setoom(s);
return false;
}
free((void*)f->package);
upb_gfree((void*)f->package);
f->package = package;
return true;
}
@ -1954,7 +2009,7 @@ bool upb_filedef_adddef(upb_filedef *f, upb_def *def, const void *ref_donor,
}
return true;
} else {
upb_status_seterrmsg(s, "Out of memory.");
upb_upberr_setoom(s);
return false;
}
}

@ -61,7 +61,7 @@ struct upb_descreader {
};
static char *upb_strndup(const char *buf, size_t n) {
char *ret = malloc(n + 1);
char *ret = upb_gmalloc(n + 1);
if (!ret) return NULL;
memcpy(ret, buf, n);
ret[n] = '\0';
@ -75,9 +75,12 @@ static char *upb_strndup(const char *buf, size_t n) {
* Caller owns a ref on the returned string. */
static char *upb_join(const char *base, const char *name) {
if (!base || strlen(base) == 0) {
return upb_strdup(name);
return upb_gstrdup(name);
} else {
char *ret = malloc(strlen(base) + strlen(name) + 2);
char *ret = upb_gmalloc(strlen(base) + strlen(name) + 2);
if (!ret) {
return NULL;
}
ret[0] = '\0';
strcat(ret, base);
strcat(ret, ".");
@ -87,14 +90,20 @@ static char *upb_join(const char *base, const char *name) {
}
/* Qualify the defname for all defs starting with offset "start" with "str". */
static void upb_descreader_qualify(upb_filedef *f, char *str, int32_t start) {
static bool upb_descreader_qualify(upb_filedef *f, char *str, int32_t start) {
size_t i;
for (i = start; i < upb_filedef_defcount(f); i++) {
upb_def *def = upb_filedef_mutabledef(f, i);
char *name = upb_join(str, upb_def_fullname(def));
if (!name) {
/* Need better logic here; at this point we've qualified some names but
* not others. */
return false;
}
upb_def_setfullname(def, name, NULL);
free(name);
upb_gfree(name);
}
return true;
}
@ -120,16 +129,19 @@ void upb_descreader_startcontainer(upb_descreader *r) {
f->name = NULL;
}
void upb_descreader_endcontainer(upb_descreader *r) {
bool upb_descreader_endcontainer(upb_descreader *r) {
upb_descreader_frame *f = &r->stack[--r->stack_len];
upb_descreader_qualify(r->file, f->name, f->start);
free(f->name);
if (!upb_descreader_qualify(r->file, f->name, f->start)) {
return false;
}
upb_gfree(f->name);
f->name = NULL;
return true;
}
void upb_descreader_setscopename(upb_descreader *r, char *str) {
upb_descreader_frame *f = &r->stack[r->stack_len-1];
free(f->name);
upb_gfree(f->name);
f->name = str;
}
@ -156,8 +168,7 @@ static bool file_end(void *closure, const void *hd, upb_status *status) {
upb_descreader *r = closure;
UPB_UNUSED(hd);
UPB_UNUSED(status);
upb_descreader_endcontainer(r);
return true;
return upb_descreader_endcontainer(r);
}
static size_t file_onname(void *closure, const void *hd, const char *buf,
@ -171,6 +182,7 @@ static size_t file_onname(void *closure, const void *hd, const char *buf,
name = upb_strndup(buf, n);
/* XXX: see comment at the top of the file. */
ok = upb_filedef_setname(r->file, name, NULL);
upb_gfree(name);
UPB_ASSERT_VAR(ok, ok);
return n;
}
@ -254,7 +266,7 @@ static size_t enumval_onname(void *closure, const void *hd, const char *buf,
UPB_UNUSED(hd);
UPB_UNUSED(handle);
/* XXX: see comment at the top of the file. */
free(r->name);
upb_gfree(r->name);
r->name = upb_strndup(buf, n);
r->saw_name = true;
return n;
@ -279,7 +291,7 @@ static bool enumval_endmsg(void *closure, const void *hd, upb_status *status) {
}
e = upb_downcast_enumdef_mutable(upb_descreader_last(r));
upb_enumdef_addval(e, r->name, r->number, status);
free(r->name);
upb_gfree(r->name);
r->name = NULL;
return true;
}
@ -311,7 +323,7 @@ static size_t enum_onname(void *closure, const void *hd, const char *buf,
UPB_UNUSED(handle);
/* XXX: see comment at the top of the file. */
upb_def_setfullname(upb_descreader_last(r), fullname, NULL);
free(fullname);
upb_gfree(fullname);
return n;
}
@ -321,7 +333,7 @@ static bool field_startmsg(void *closure, const void *hd) {
upb_descreader *r = closure;
UPB_UNUSED(hd);
assert(r->f);
free(r->default_string);
upb_gfree(r->default_string);
r->default_string = NULL;
/* fielddefs default to packed, but descriptors default to non-packed. */
@ -480,7 +492,7 @@ static size_t field_onname(void *closure, const void *hd, const char *buf,
/* XXX: see comment at the top of the file. */
upb_fielddef_setname(r->f, name, NULL);
free(name);
upb_gfree(name);
return n;
}
@ -493,7 +505,7 @@ static size_t field_ontypename(void *closure, const void *hd, const char *buf,
/* XXX: see comment at the top of the file. */
upb_fielddef_setsubdefname(r->f, name, NULL);
free(name);
upb_gfree(name);
return n;
}
@ -506,7 +518,7 @@ static size_t field_onextendee(void *closure, const void *hd, const char *buf,
/* XXX: see comment at the top of the file. */
upb_fielddef_setcontainingtypename(r->f, name, NULL);
free(name);
upb_gfree(name);
return n;
}
@ -519,7 +531,7 @@ static size_t field_ondefaultval(void *closure, const void *hd, const char *buf,
/* Have to convert from string to the correct type, but we might not know the
* type yet, so we save it as a string until the end of the field.
* XXX: see comment at the top of the file. */
free(r->default_string);
upb_gfree(r->default_string);
r->default_string = upb_strndup(buf, n);
return n;
}
@ -543,8 +555,7 @@ static bool msg_end(void *closure, const void *hd, upb_status *status) {
upb_status_seterrmsg(status, "Encountered message with no name.");
return false;
}
upb_descreader_endcontainer(r);
return true;
return upb_descreader_endcontainer(r);
}
static size_t msg_name(void *closure, const void *hd, const char *buf,
@ -683,12 +694,12 @@ void descreader_cleanup(void *_r) {
upb_filedef_unref(upb_descreader_file(r, i), &r->files);
}
free(r->name);
upb_gfree(r->name);
upb_inttable_uninit(&r->files);
free(r->default_string);
upb_gfree(r->default_string);
while (r->stack_len > 0) {
upb_descreader_frame *f = &r->stack[--r->stack_len];
free(f->name);
upb_gfree(f->name);
}
}

@ -7,7 +7,6 @@
#ifndef UPB_DESCRIPTOR_H
#define UPB_DESCRIPTOR_H
#include "upb/env.h"
#include "upb/sink.h"
#ifdef __cplusplus

@ -1,273 +0,0 @@
#include "upb/env.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct cleanup_ent {
upb_cleanup_func *cleanup;
void *ud;
struct cleanup_ent *next;
} cleanup_ent;
static void *seeded_alloc(void *ud, void *ptr, size_t oldsize, size_t size);
/* Default allocator **********************************************************/
/* Just use realloc, keeping all allocated blocks in a linked list to destroy at
* the end. */
typedef struct mem_block {
/* List is doubly-linked, because in cases where realloc() moves an existing
* block, we need to be able to remove the old pointer from the list
* efficiently. */
struct mem_block *prev, *next;
#ifndef NDEBUG
size_t size; /* Doesn't include mem_block structure. */
#endif
} mem_block;
typedef struct {
mem_block *head;
} default_alloc_ud;
static void *default_alloc(void *_ud, void *ptr, size_t oldsize, size_t size) {
default_alloc_ud *ud = _ud;
mem_block *from, *block;
void *ret;
UPB_UNUSED(oldsize);
from = ptr ? (void*)((char*)ptr - sizeof(mem_block)) : NULL;
#ifndef NDEBUG
if (from) {
assert(oldsize <= from->size);
}
#endif
/* TODO(haberman): we probably need to provide even better alignment here,
* like 16-byte alignment of the returned data pointer. */
block = realloc(from, size + sizeof(mem_block));
if (!block) return NULL;
ret = (char*)block + sizeof(*block);
#ifndef NDEBUG
block->size = size;
#endif
if (from) {
if (block != from) {
/* The block was moved, so pointers in next and prev blocks must be
* updated to its new location. */
if (block->next) block->next->prev = block;
if (block->prev) block->prev->next = block;
if (ud->head == from) ud->head = block;
}
} else {
/* Insert at head of linked list. */
block->prev = NULL;
block->next = ud->head;
if (block->next) block->next->prev = block;
ud->head = block;
}
return ret;
}
static void default_alloc_cleanup(void *_ud) {
default_alloc_ud *ud = _ud;
mem_block *block = ud->head;
while (block) {
void *to_free = block;
block = block->next;
free(to_free);
}
}
/* Standard error functions ***************************************************/
static bool default_err(void *ud, const upb_status *status) {
UPB_UNUSED(ud);
UPB_UNUSED(status);
return false;
}
static bool write_err_to(void *ud, const upb_status *status) {
upb_status *copy_to = ud;
upb_status_copy(copy_to, status);
return false;
}
/* upb_env ********************************************************************/
void upb_env_init(upb_env *e) {
default_alloc_ud *ud = (default_alloc_ud*)&e->default_alloc_ud;
e->ok_ = true;
e->bytes_allocated = 0;
e->cleanup_head = NULL;
ud->head = NULL;
/* Set default functions. */
upb_env_setallocfunc(e, default_alloc, ud);
upb_env_seterrorfunc(e, default_err, NULL);
}
void upb_env_uninit(upb_env *e) {
cleanup_ent *ent = e->cleanup_head;
while (ent) {
ent->cleanup(ent->ud);
ent = ent->next;
}
/* Must do this after running cleanup functions, because this will delete
the memory we store our cleanup entries in! */
if (e->alloc == default_alloc) {
default_alloc_cleanup(e->alloc_ud);
}
}
UPB_FORCEINLINE void upb_env_setallocfunc(upb_env *e, upb_alloc_func *alloc,
void *ud) {
e->alloc = alloc;
e->alloc_ud = ud;
}
UPB_FORCEINLINE void upb_env_seterrorfunc(upb_env *e, upb_error_func *func,
void *ud) {
e->err = func;
e->err_ud = ud;
}
void upb_env_reporterrorsto(upb_env *e, upb_status *status) {
e->err = write_err_to;
e->err_ud = status;
}
bool upb_env_ok(const upb_env *e) {
return e->ok_;
}
bool upb_env_reporterror(upb_env *e, const upb_status *status) {
e->ok_ = false;
return e->err(e->err_ud, status);
}
bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud) {
cleanup_ent *ent = upb_env_malloc(e, sizeof(cleanup_ent));
if (!ent) return false;
ent->cleanup = func;
ent->ud = ud;
ent->next = e->cleanup_head;
e->cleanup_head = ent;
return true;
}
void *upb_env_malloc(upb_env *e, size_t size) {
e->bytes_allocated += size;
if (e->alloc == seeded_alloc) {
/* This is equivalent to the next branch, but allows inlining for a
* measurable perf benefit. */
return seeded_alloc(e->alloc_ud, NULL, 0, size);
} else {
return e->alloc(e->alloc_ud, NULL, 0, size);
}
}
void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size) {
char *ret;
assert(oldsize <= size);
ret = e->alloc(e->alloc_ud, ptr, oldsize, size);
#ifndef NDEBUG
/* Overwrite non-preserved memory to ensure callers are passing the oldsize
* that they truly require. */
memset(ret + oldsize, 0xff, size - oldsize);
#endif
return ret;
}
size_t upb_env_bytesallocated(const upb_env *e) {
return e->bytes_allocated;
}
/* upb_seededalloc ************************************************************/
/* Be conservative and choose 16 in case anyone is using SSE. */
static const size_t maxalign = 16;
static size_t align_up(size_t size) {
return ((size + maxalign - 1) / maxalign) * maxalign;
}
UPB_FORCEINLINE static void *seeded_alloc(void *ud, void *ptr, size_t oldsize,
size_t size) {
upb_seededalloc *a = ud;
size = align_up(size);
assert(a->mem_limit >= a->mem_ptr);
if (oldsize == 0 && size <= (size_t)(a->mem_limit - a->mem_ptr)) {
/* Fast path: we can satisfy from the initial allocation. */
void *ret = a->mem_ptr;
a->mem_ptr += size;
return ret;
} else {
char *chptr = ptr;
/* Slow path: fallback to other allocator. */
a->need_cleanup = true;
/* Is `ptr` part of the user-provided initial block? Don't pass it to the
* default allocator if so; otherwise, it may try to realloc() the block. */
if (chptr >= a->mem_base && chptr < a->mem_limit) {
void *ret;
assert(chptr + oldsize <= a->mem_limit);
ret = a->alloc(a->alloc_ud, NULL, 0, size);
if (ret) memcpy(ret, ptr, oldsize);
return ret;
} else {
return a->alloc(a->alloc_ud, ptr, oldsize, size);
}
}
}
void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len) {
default_alloc_ud *ud = (default_alloc_ud*)&a->default_alloc_ud;
a->mem_base = mem;
a->mem_ptr = mem;
a->mem_limit = (char*)mem + len;
a->need_cleanup = false;
a->returned_allocfunc = false;
ud->head = NULL;
upb_seededalloc_setfallbackalloc(a, default_alloc, ud);
}
void upb_seededalloc_uninit(upb_seededalloc *a) {
if (a->alloc == default_alloc && a->need_cleanup) {
default_alloc_cleanup(a->alloc_ud);
}
}
UPB_FORCEINLINE void upb_seededalloc_setfallbackalloc(upb_seededalloc *a,
upb_alloc_func *alloc,
void *ud) {
assert(!a->returned_allocfunc);
a->alloc = alloc;
a->alloc_ud = ud;
}
upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a) {
a->returned_allocfunc = true;
return seeded_alloc;
}

@ -1,262 +0,0 @@
/*
** upb::Environment (upb_env)
**
** A upb::Environment provides a means for injecting malloc and an
** error-reporting callback into encoders/decoders. This allows them to be
** independent of nearly all assumptions about their actual environment.
**
** It is also a container for allocating the encoders/decoders themselves that
** insulates clients from knowing their actual size. This provides ABI
** compatibility even if the size of the objects change. And this allows the
** structure definitions to be in the .c files instead of the .h files, making
** the .h files smaller and more readable.
*/
#include "upb/upb.h"
#ifndef UPB_ENV_H_
#define UPB_ENV_H_
#ifdef __cplusplus
namespace upb {
class Environment;
class SeededAllocator;
}
#endif
UPB_DECLARE_TYPE(upb::Environment, upb_env)
UPB_DECLARE_TYPE(upb::SeededAllocator, upb_seededalloc)
typedef void *upb_alloc_func(void *ud, void *ptr, size_t oldsize, size_t size);
typedef void upb_cleanup_func(void *ud);
typedef bool upb_error_func(void *ud, const upb_status *status);
#ifdef __cplusplus
/* An environment is *not* thread-safe. */
class upb::Environment {
public:
Environment();
~Environment();
/* Set a custom memory allocation function for the environment. May ONLY
* be called before any calls to Malloc()/Realloc()/AddCleanup() below.
* If this is not called, the system realloc() function will be used.
* The given user pointer "ud" will be passed to the allocation function.
*
* The allocation function will not receive corresponding "free" calls. it
* must ensure that the memory is valid for the lifetime of the Environment,
* but it may be reclaimed any time thereafter. The likely usage is that
* "ud" points to a stateful allocator, and that the allocator frees all
* memory, arena-style, when it is destroyed. In this case the allocator must
* outlive the Environment. Another possibility is that the allocation
* function returns GC-able memory that is guaranteed to be GC-rooted for the
* life of the Environment. */
void SetAllocationFunction(upb_alloc_func* alloc, void* ud);
template<class T>
void SetAllocator(T* allocator) {
SetAllocationFunction(allocator->GetAllocationFunction(), allocator);
}
/* Set a custom error reporting function. */
void SetErrorFunction(upb_error_func* func, void* ud);
/* Set the error reporting function to simply copy the status to the given
* status and abort. */
void ReportErrorsTo(Status* status);
/* Returns true if all allocations and AddCleanup() calls have succeeded,
* and no errors were reported with ReportError() (except ones that recovered
* successfully). */
bool ok() const;
/* Functions for use by encoders/decoders. **********************************/
/* Reports an error to this environment's callback, returning true if
* the caller should try to recover. */
bool ReportError(const Status* status);
/* Allocate memory. Uses the environment's allocation function.
*
* There is no need to free(). All memory will be freed automatically, but is
* guaranteed to outlive the Environment. */
void* Malloc(size_t size);
/* Reallocate memory. Preserves "oldsize" bytes from the existing buffer
* Requires: oldsize <= existing_size.
*
* TODO(haberman): should we also enforce that oldsize <= size? */
void* Realloc(void* ptr, size_t oldsize, size_t size);
/* Add a cleanup function to run when the environment is destroyed.
* Returns false on out-of-memory.
*
* The first call to AddCleanup() after SetAllocationFunction() is guaranteed
* to return true -- this makes it possible to robustly set a cleanup handler
* for a custom allocation function. */
bool AddCleanup(upb_cleanup_func* func, void* ud);
/* Total number of bytes that have been allocated. It is undefined what
* Realloc() does to this counter. */
size_t BytesAllocated() const;
private:
UPB_DISALLOW_COPY_AND_ASSIGN(Environment)
#else
struct upb_env {
#endif /* __cplusplus */
bool ok_;
size_t bytes_allocated;
/* Alloc function. */
upb_alloc_func *alloc;
void *alloc_ud;
/* Error-reporting function. */
upb_error_func *err;
void *err_ud;
/* Userdata for default alloc func. */
void *default_alloc_ud;
/* Cleanup entries. Pointer to a cleanup_ent, defined in env.c */
void *cleanup_head;
/* For future expansion, since the size of this struct is exposed to users. */
void *future1;
void *future2;
};
UPB_BEGIN_EXTERN_C
void upb_env_init(upb_env *e);
void upb_env_uninit(upb_env *e);
void upb_env_setallocfunc(upb_env *e, upb_alloc_func *func, void *ud);
void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud);
void upb_env_reporterrorsto(upb_env *e, upb_status *status);
bool upb_env_ok(const upb_env *e);
bool upb_env_reporterror(upb_env *e, const upb_status *status);
void *upb_env_malloc(upb_env *e, size_t size);
void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size);
bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud);
size_t upb_env_bytesallocated(const upb_env *e);
UPB_END_EXTERN_C
#ifdef __cplusplus
/* An allocator that allocates from an initial memory region (likely the stack)
* before falling back to another allocator. */
class upb::SeededAllocator {
public:
SeededAllocator(void *mem, size_t len);
~SeededAllocator();
/* Set a custom fallback memory allocation function for the allocator, to use
* once the initial region runs out.
*
* May ONLY be called before GetAllocationFunction(). If this is not
* called, the system realloc() will be the fallback allocator. */
void SetFallbackAllocator(upb_alloc_func *alloc, void *ud);
/* Gets the allocation function for this allocator. */
upb_alloc_func* GetAllocationFunction();
private:
UPB_DISALLOW_COPY_AND_ASSIGN(SeededAllocator)
#else
struct upb_seededalloc {
#endif /* __cplusplus */
/* Fallback alloc function. */
upb_alloc_func *alloc;
upb_cleanup_func *alloc_cleanup;
void *alloc_ud;
bool need_cleanup;
bool returned_allocfunc;
/* Userdata for default alloc func. */
void *default_alloc_ud;
/* Pointers for the initial memory region. */
char *mem_base;
char *mem_ptr;
char *mem_limit;
/* For future expansion, since the size of this struct is exposed to users. */
void *future1;
void *future2;
};
UPB_BEGIN_EXTERN_C
void upb_seededalloc_init(upb_seededalloc *a, void *mem, size_t len);
void upb_seededalloc_uninit(upb_seededalloc *a);
void upb_seededalloc_setfallbackalloc(upb_seededalloc *a, upb_alloc_func *func,
void *ud);
upb_alloc_func *upb_seededalloc_getallocfunc(upb_seededalloc *a);
UPB_END_EXTERN_C
#ifdef __cplusplus
namespace upb {
inline Environment::Environment() {
upb_env_init(this);
}
inline Environment::~Environment() {
upb_env_uninit(this);
}
inline void Environment::SetAllocationFunction(upb_alloc_func *alloc,
void *ud) {
upb_env_setallocfunc(this, alloc, ud);
}
inline void Environment::SetErrorFunction(upb_error_func *func, void *ud) {
upb_env_seterrorfunc(this, func, ud);
}
inline void Environment::ReportErrorsTo(Status* status) {
upb_env_reporterrorsto(this, status);
}
inline bool Environment::ok() const {
return upb_env_ok(this);
}
inline bool Environment::ReportError(const Status* status) {
return upb_env_reporterror(this, status);
}
inline void *Environment::Malloc(size_t size) {
return upb_env_malloc(this, size);
}
inline void *Environment::Realloc(void *ptr, size_t oldsize, size_t size) {
return upb_env_realloc(this, ptr, oldsize, size);
}
inline bool Environment::AddCleanup(upb_cleanup_func *func, void *ud) {
return upb_env_addcleanup(this, func, ud);
}
inline size_t Environment::BytesAllocated() const {
return upb_env_bytesallocated(this);
}
inline SeededAllocator::SeededAllocator(void *mem, size_t len) {
upb_seededalloc_init(this, mem, len);
}
inline SeededAllocator::~SeededAllocator() {
upb_seededalloc_uninit(this);
}
inline void SeededAllocator::SetFallbackAllocator(upb_alloc_func *alloc,
void *ud) {
upb_seededalloc_setfallbackalloc(this, alloc, ud);
}
inline upb_alloc_func *SeededAllocator::GetAllocationFunction() {
return upb_seededalloc_getallocfunc(this);
}
} /* namespace upb */
#endif /* __cplusplus */
#endif /* UPB_ENV_H_ */

@ -6,11 +6,17 @@
#include "upb/handlers.h"
#include "upb/structdefs.int.h"
#include <stdlib.h>
#include <string.h>
#include "upb/sink.h"
static void *upb_calloc(size_t size) {
void *mem = upb_gmalloc(size);
if (mem) {
memset(mem, 0, size);
}
return mem;
}
/* Defined for the sole purpose of having a unique pointer value for
* UPB_NO_CLOSURE. */
@ -30,8 +36,8 @@ static void freehandlers(upb_refcounted *r) {
upb_inttable_uninit(&h->cleanup_);
upb_msgdef_unref(h->msg, h);
free(h->sub);
free(h);
upb_gfree(h->sub);
upb_gfree(h);
}
static void visithandlers(const upb_refcounted *r, upb_refcounted_visit *visit,
@ -281,14 +287,20 @@ upb_handlers *upb_handlers_new(const upb_msgdef *md, const void *owner) {
assert(upb_msgdef_isfrozen(md));
extra = sizeof(upb_handlers_tabent) * (md->selector_count - 1);
h = calloc(sizeof(*h) + extra, 1);
h = upb_calloc(sizeof(*h) + extra);
if (!h) return NULL;
h->msg = md;
upb_msgdef_ref(h->msg, h);
upb_status_clear(&h->status_);
h->sub = calloc(md->submsg_field_count, sizeof(*h->sub));
if (md->submsg_field_count > 0) {
h->sub = upb_calloc(md->submsg_field_count * sizeof(*h->sub));
if (!h->sub) goto oom;
} else {
h->sub = 0;
}
if (!upb_refcounted_init(upb_handlers_upcast_mutable(h), &vtbl, owner))
goto oom;
if (!upb_inttable_init(&h->cleanup_, UPB_CTYPE_FPTR)) goto oom;

@ -21,12 +21,11 @@
** - handling of keys/escape-sequences/etc that span input buffers.
*/
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "upb/json/parser.h"
@ -1150,11 +1149,11 @@ static void end_object(upb_json_parser *p) {
* final state once, when the closing '"' is seen. */
#line 1246 "upb/json/parser.rl"
#line 1245 "upb/json/parser.rl"
#line 1158 "upb/json/parser.c"
#line 1157 "upb/json/parser.c"
static const char _json_actions[] = {
0, 1, 0, 1, 2, 1, 3, 1,
5, 1, 6, 1, 7, 1, 8, 1,
@ -1303,7 +1302,7 @@ static const int json_en_value_machine = 27;
static const int json_en_main = 1;
#line 1249 "upb/json/parser.rl"
#line 1248 "upb/json/parser.rl"
size_t parse(void *closure, const void *hd, const char *buf, size_t size,
const upb_bufhandle *handle) {
@ -1325,7 +1324,7 @@ size_t parse(void *closure, const void *hd, const char *buf, size_t size,
capture_resume(parser, buf);
#line 1329 "upb/json/parser.c"
#line 1328 "upb/json/parser.c"
{
int _klen;
unsigned int _trans;
@ -1400,118 +1399,118 @@ _match:
switch ( *_acts++ )
{
case 0:
#line 1161 "upb/json/parser.rl"
#line 1160 "upb/json/parser.rl"
{ p--; {cs = stack[--top]; goto _again;} }
break;
case 1:
#line 1162 "upb/json/parser.rl"
#line 1161 "upb/json/parser.rl"
{ p--; {stack[top++] = cs; cs = 10; goto _again;} }
break;
case 2:
#line 1166 "upb/json/parser.rl"
#line 1165 "upb/json/parser.rl"
{ start_text(parser, p); }
break;
case 3:
#line 1167 "upb/json/parser.rl"
#line 1166 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_text(parser, p)); }
break;
case 4:
#line 1173 "upb/json/parser.rl"
#line 1172 "upb/json/parser.rl"
{ start_hex(parser); }
break;
case 5:
#line 1174 "upb/json/parser.rl"
#line 1173 "upb/json/parser.rl"
{ hexdigit(parser, p); }
break;
case 6:
#line 1175 "upb/json/parser.rl"
#line 1174 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_hex(parser)); }
break;
case 7:
#line 1181 "upb/json/parser.rl"
#line 1180 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(escape(parser, p)); }
break;
case 8:
#line 1187 "upb/json/parser.rl"
#line 1186 "upb/json/parser.rl"
{ p--; {cs = stack[--top]; goto _again;} }
break;
case 9:
#line 1190 "upb/json/parser.rl"
#line 1189 "upb/json/parser.rl"
{ {stack[top++] = cs; cs = 19; goto _again;} }
break;
case 10:
#line 1192 "upb/json/parser.rl"
#line 1191 "upb/json/parser.rl"
{ p--; {stack[top++] = cs; cs = 27; goto _again;} }
break;
case 11:
#line 1197 "upb/json/parser.rl"
#line 1196 "upb/json/parser.rl"
{ start_member(parser); }
break;
case 12:
#line 1198 "upb/json/parser.rl"
#line 1197 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_membername(parser)); }
break;
case 13:
#line 1201 "upb/json/parser.rl"
#line 1200 "upb/json/parser.rl"
{ end_member(parser); }
break;
case 14:
#line 1207 "upb/json/parser.rl"
#line 1206 "upb/json/parser.rl"
{ start_object(parser); }
break;
case 15:
#line 1210 "upb/json/parser.rl"
#line 1209 "upb/json/parser.rl"
{ end_object(parser); }
break;
case 16:
#line 1216 "upb/json/parser.rl"
#line 1215 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(start_array(parser)); }
break;
case 17:
#line 1220 "upb/json/parser.rl"
#line 1219 "upb/json/parser.rl"
{ end_array(parser); }
break;
case 18:
#line 1225 "upb/json/parser.rl"
#line 1224 "upb/json/parser.rl"
{ start_number(parser, p); }
break;
case 19:
#line 1226 "upb/json/parser.rl"
#line 1225 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_number(parser, p)); }
break;
case 20:
#line 1228 "upb/json/parser.rl"
#line 1227 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(start_stringval(parser)); }
break;
case 21:
#line 1229 "upb/json/parser.rl"
#line 1228 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(end_stringval(parser)); }
break;
case 22:
#line 1231 "upb/json/parser.rl"
#line 1230 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(parser_putbool(parser, true)); }
break;
case 23:
#line 1233 "upb/json/parser.rl"
#line 1232 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(parser_putbool(parser, false)); }
break;
case 24:
#line 1235 "upb/json/parser.rl"
#line 1234 "upb/json/parser.rl"
{ /* null value */ }
break;
case 25:
#line 1237 "upb/json/parser.rl"
#line 1236 "upb/json/parser.rl"
{ CHECK_RETURN_TOP(start_subobject(parser)); }
break;
case 26:
#line 1238 "upb/json/parser.rl"
#line 1237 "upb/json/parser.rl"
{ end_subobject(parser); }
break;
case 27:
#line 1243 "upb/json/parser.rl"
#line 1242 "upb/json/parser.rl"
{ p--; {cs = stack[--top]; goto _again;} }
break;
#line 1515 "upb/json/parser.c"
#line 1514 "upb/json/parser.c"
}
}
@ -1524,7 +1523,7 @@ _again:
_out: {}
}
#line 1270 "upb/json/parser.rl"
#line 1269 "upb/json/parser.rl"
if (p != pe) {
upb_status_seterrf(&parser->status, "Parse error at '%.*s'\n", pe - p, p);
@ -1565,13 +1564,13 @@ static void json_parser_reset(upb_json_parser *p) {
/* Emit Ragel initialization of the parser. */
#line 1569 "upb/json/parser.c"
#line 1568 "upb/json/parser.c"
{
cs = json_start;
top = 0;
}
#line 1310 "upb/json/parser.rl"
#line 1309 "upb/json/parser.rl"
p->current_state = cs;
p->parser_top = top;
accumulate_clear(p);
@ -1597,12 +1596,12 @@ static void free_json_parsermethod(upb_refcounted *r) {
upb_value val = upb_inttable_iter_value(&i);
upb_strtable *t = upb_value_getptr(val);
upb_strtable_uninit(t);
free(t);
upb_gfree(t);
}
upb_inttable_uninit(&method->name_tables);
free(r);
upb_gfree(r);
}
static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) {
@ -1619,7 +1618,7 @@ static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) {
}
/* TODO(haberman): handle malloc failure. */
t = malloc(sizeof(*t));
t = upb_gmalloc(sizeof(*t));
upb_strtable_init(t, UPB_CTYPE_CONSTPTR);
upb_inttable_insertptr(&m->name_tables, md, upb_value_ptr(t));
@ -1632,7 +1631,7 @@ static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) {
size_t field_len = upb_fielddef_getjsonname(f, buf, len);
if (field_len > len) {
size_t len2;
buf = realloc(buf, field_len);
buf = upb_grealloc(buf, 0, field_len);
len = field_len;
len2 = upb_fielddef_getjsonname(f, buf, len);
UPB_ASSERT_VAR(len2, len == len2);
@ -1651,7 +1650,7 @@ static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) {
}
}
free(buf);
upb_gfree(buf);
}
/* Public API *****************************************************************/
@ -1691,7 +1690,7 @@ upb_json_parsermethod *upb_json_parsermethod_new(const upb_msgdef* md,
const void* owner) {
static const struct upb_refcounted_vtbl vtbl = {visit_json_parsermethod,
free_json_parsermethod};
upb_json_parsermethod *ret = malloc(sizeof(*ret));
upb_json_parsermethod *ret = upb_gmalloc(sizeof(*ret));
upb_refcounted_init(upb_json_parsermethod_upcast_mutable(ret), &vtbl, owner);
ret->msg = md;

@ -8,7 +8,6 @@
#ifndef UPB_JSON_PARSER_H_
#define UPB_JSON_PARSER_H_
#include "upb/env.h"
#include "upb/sink.h"
#ifdef __cplusplus
@ -30,7 +29,7 @@ UPB_DECLARE_DERIVED_TYPE(upb::json::ParserMethod, upb::RefCounted,
* constructed. This hint may be an overestimate for some build configurations.
* But if the parser library is upgraded without recompiling the application,
* it may be an underestimate. */
#define UPB_JSON_PARSER_SIZE 4104
#define UPB_JSON_PARSER_SIZE 4112
#ifdef __cplusplus

@ -19,12 +19,11 @@
** - handling of keys/escape-sequences/etc that span input buffers.
*/
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "upb/json/parser.h"
@ -1332,12 +1331,12 @@ static void free_json_parsermethod(upb_refcounted *r) {
upb_value val = upb_inttable_iter_value(&i);
upb_strtable *t = upb_value_getptr(val);
upb_strtable_uninit(t);
free(t);
upb_gfree(t);
}
upb_inttable_uninit(&method->name_tables);
free(r);
upb_gfree(r);
}
static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) {
@ -1354,7 +1353,7 @@ static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) {
}
/* TODO(haberman): handle malloc failure. */
t = malloc(sizeof(*t));
t = upb_gmalloc(sizeof(*t));
upb_strtable_init(t, UPB_CTYPE_CONSTPTR);
upb_inttable_insertptr(&m->name_tables, md, upb_value_ptr(t));
@ -1367,7 +1366,7 @@ static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) {
size_t field_len = upb_fielddef_getjsonname(f, buf, len);
if (field_len > len) {
size_t len2;
buf = realloc(buf, field_len);
buf = upb_grealloc(buf, 0, field_len);
len = field_len;
len2 = upb_fielddef_getjsonname(f, buf, len);
UPB_ASSERT_VAR(len2, len == len2);
@ -1386,7 +1385,7 @@ static void add_jsonname_table(upb_json_parsermethod *m, const upb_msgdef* md) {
}
}
free(buf);
upb_gfree(buf);
}
/* Public API *****************************************************************/
@ -1426,7 +1425,7 @@ upb_json_parsermethod *upb_json_parsermethod_new(const upb_msgdef* md,
const void* owner) {
static const struct upb_refcounted_vtbl vtbl = {visit_json_parsermethod,
free_json_parsermethod};
upb_json_parsermethod *ret = malloc(sizeof(*ret));
upb_json_parsermethod *ret = upb_gmalloc(sizeof(*ret));
upb_refcounted_init(upb_json_parsermethod_upcast_mutable(ret), &vtbl, owner);
ret->msg = md;

@ -5,8 +5,6 @@
#include "upb/json/printer.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
@ -39,22 +37,22 @@ typedef struct {
void freestrpc(void *ptr) {
strpc *pc = ptr;
free(pc->ptr);
free(pc);
upb_gfree(pc->ptr);
upb_gfree(pc);
}
/* Convert fielddef name to JSON name and return as a string piece. */
strpc *newstrpc(upb_handlers *h, const upb_fielddef *f,
bool preserve_fieldnames) {
/* TODO(haberman): handle malloc failure. */
strpc *ret = malloc(sizeof(*ret));
strpc *ret = upb_gmalloc(sizeof(*ret));
if (preserve_fieldnames) {
ret->ptr = upb_strdup(upb_fielddef_name(f));
ret->ptr = upb_gstrdup(upb_fielddef_name(f));
ret->len = strlen(ret->ptr);
} else {
size_t len;
ret->len = upb_fielddef_getjsonname(f, NULL, 0);
ret->ptr = malloc(ret->len);
ret->ptr = upb_gmalloc(ret->len);
len = upb_fielddef_getjsonname(f, ret->ptr, ret->len);
UPB_ASSERT_VAR(len, len == ret->len);
ret->len--; /* NULL */
@ -566,10 +564,10 @@ static void set_enum_hd(upb_handlers *h,
const upb_fielddef *f,
bool preserve_fieldnames,
upb_handlerattr *attr) {
EnumHandlerData *hd = malloc(sizeof(EnumHandlerData));
EnumHandlerData *hd = upb_gmalloc(sizeof(EnumHandlerData));
hd->enumdef = (const upb_enumdef *)upb_fielddef_subdef(f);
hd->keyname = newstrpc(h, f, preserve_fieldnames);
upb_handlers_addcleanup(h, hd, free);
upb_handlers_addcleanup(h, hd, upb_gfree);
upb_handlerattr_sethandlerdata(attr, hd);
}

@ -7,7 +7,6 @@
#ifndef UPB_JSON_TYPED_PRINTER_H_
#define UPB_JSON_TYPED_PRINTER_H_
#include "upb/env.h"
#include "upb/sink.h"
#ifdef __cplusplus
@ -23,7 +22,7 @@ UPB_DECLARE_TYPE(upb::json::Printer, upb_json_printer)
/* upb::json::Printer *********************************************************/
#define UPB_JSON_PRINTER_SIZE 168
#define UPB_JSON_PRINTER_SIZE 176
#ifdef __cplusplus

@ -31,8 +31,8 @@ static void freegroup(upb_refcounted *r) {
#ifdef UPB_USE_JIT_X64
upb_pbdecoder_freejit(g);
#endif
free(g->bytecode);
free(g);
upb_gfree(g->bytecode);
upb_gfree(g);
}
static void visitgroup(const upb_refcounted *r, upb_refcounted_visit *visit,
@ -47,7 +47,7 @@ static void visitgroup(const upb_refcounted *r, upb_refcounted_visit *visit,
}
mgroup *newgroup(const void *owner) {
mgroup *g = malloc(sizeof(*g));
mgroup *g = upb_gmalloc(sizeof(*g));
static const struct upb_refcounted_vtbl vtbl = {visitgroup, freegroup};
upb_refcounted_init(mgroup_upcast_mutable(g), &vtbl, owner);
upb_inttable_init(&g->methods, UPB_CTYPE_PTR);
@ -67,7 +67,7 @@ static void freemethod(upb_refcounted *r) {
}
upb_inttable_uninit(&method->dispatch);
free(method);
upb_gfree(method);
}
static void visitmethod(const upb_refcounted *r, upb_refcounted_visit *visit,
@ -79,7 +79,7 @@ static void visitmethod(const upb_refcounted *r, upb_refcounted_visit *visit,
static upb_pbdecodermethod *newmethod(const upb_handlers *dest_handlers,
mgroup *group) {
static const struct upb_refcounted_vtbl vtbl = {visitmethod, freemethod};
upb_pbdecodermethod *ret = malloc(sizeof(*ret));
upb_pbdecodermethod *ret = upb_gmalloc(sizeof(*ret));
upb_refcounted_init(upb_pbdecodermethod_upcast_mutable(ret), &vtbl, &ret);
upb_byteshandler_init(&ret->input_handler_);
@ -142,7 +142,7 @@ typedef struct {
} compiler;
static compiler *newcompiler(mgroup *group, bool lazy) {
compiler *ret = malloc(sizeof(*ret));
compiler *ret = upb_gmalloc(sizeof(*ret));
int i;
ret->group = group;
@ -155,7 +155,7 @@ static compiler *newcompiler(mgroup *group, bool lazy) {
}
static void freecompiler(compiler *c) {
free(c);
upb_gfree(c);
}
const size_t ptr_words = sizeof(void*) / sizeof(uint32_t);
@ -259,7 +259,8 @@ static void put32(compiler *c, uint32_t v) {
size_t oldsize = g->bytecode_end - g->bytecode;
size_t newsize = UPB_MAX(oldsize * 2, 64);
/* TODO(haberman): handle OOM. */
g->bytecode = realloc(g->bytecode, newsize * sizeof(uint32_t));
g->bytecode = upb_grealloc(g->bytecode, oldsize * sizeof(uint32_t),
newsize * sizeof(uint32_t));
g->bytecode_end = g->bytecode + newsize;
c->pc = g->bytecode + ofs;
}

@ -15,7 +15,6 @@
#ifndef UPB_DECODER_H_
#define UPB_DECODER_H_
#include "upb/env.h"
#include "upb/sink.h"
#ifdef __cplusplus
@ -99,7 +98,7 @@ class upb::pb::DecoderMethod {
* constructed. This hint may be an overestimate for some build configurations.
* But if the decoder library is upgraded without recompiling the application,
* it may be an underestimate. */
#define UPB_PB_DECODER_SIZE 4408
#define UPB_PB_DECODER_SIZE 4416
#ifdef __cplusplus

@ -5,7 +5,6 @@
#ifndef UPB_DECODER_INT_H_
#define UPB_DECODER_INT_H_
#include <stdlib.h>
#include "upb/def.h"
#include "upb/handlers.h"
#include "upb/pb/decoder.h"

@ -57,7 +57,6 @@
#include "upb/pb/encoder.h"
#include "upb/pb/varint.int.h"
#include <stdlib.h>
/* The output buffer is divided into segments; a segment is a string of data
* that is "ready to go" -- it does not need any varint lengths inserted into
@ -302,12 +301,12 @@ static void new_tag(upb_handlers *h, const upb_fielddef *f, upb_wiretype_t wt,
upb_handlerattr *attr) {
uint32_t n = upb_fielddef_number(f);
tag_t *tag = malloc(sizeof(tag_t));
tag_t *tag = upb_gmalloc(sizeof(tag_t));
tag->bytes = upb_vencode64((n << 3) | wt, tag->tag);
upb_handlerattr_init(attr);
upb_handlerattr_sethandlerdata(attr, tag);
upb_handlers_addcleanup(h, tag, free);
upb_handlers_addcleanup(h, tag, upb_gfree);
}
static bool encode_tag(upb_pb_encoder *e, const tag_t *tag) {

@ -12,7 +12,6 @@
#ifndef UPB_ENCODER_H_
#define UPB_ENCODER_H_
#include "upb/env.h"
#include "upb/sink.h"
#ifdef __cplusplus

@ -1,9 +1,6 @@
#include "upb/pb/glue.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "upb/descriptor/reader.h"
#include "upb/pb/decoder.h"
@ -36,7 +33,7 @@ upb_filedef **upb_loaddescriptor(const char *buf, size_t n, const void *owner,
goto cleanup;
}
ret = malloc(sizeof (*ret) * (upb_descreader_filecount(reader) + 1));
ret = upb_gmalloc(sizeof (*ret) * (upb_descreader_filecount(reader) + 1));
if (!ret) {
goto cleanup;

@ -31,7 +31,8 @@ extern "C" {
#endif
/* Loads a binary descriptor and returns a NULL-terminated array of unfrozen
* filedefs. The caller owns the returned array. */
* filedefs. The caller owns the returned array, which must be freed with
* upb_gfree(). */
upb_filedef **upb_loaddescriptor(const char *buf, size_t n, const void *owner,
upb_status *status);

@ -12,7 +12,6 @@
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "upb/sink.h"
@ -109,14 +108,14 @@ bool putf(upb_textprinter *p, const char *fmt, ...) {
va_end(args_copy);
/* + 1 for NULL terminator (vsprintf() requires it even if we don't). */
str = malloc(len + 1);
str = upb_gmalloc(len + 1);
if (!str) return false;
written = vsprintf(str, fmt, args);
va_end(args);
UPB_ASSERT_VAR(written, written == len);
ok = upb_bytessink_putbuf(p->output_, p->subc, str, len, NULL);
free(str);
upb_gfree(str);
return ok;
}

@ -7,7 +7,6 @@
#ifndef UPB_TEXT_H_
#define UPB_TEXT_H_
#include "upb/env.h"
#include "upb/sink.h"
#ifdef __cplusplus

@ -18,7 +18,6 @@
#include "upb/refcounted.h"
#include <setjmp.h>
#include <stdlib.h>
static void freeobj(upb_refcounted *o);
@ -94,8 +93,31 @@ void upb_unlock();
/* UPB_DEBUG_REFS mode counts on being able to malloc() memory in some
* code-paths that can normally never fail, like upb_refcounted_ref(). Since
* we have no way to propagage out-of-memory errors back to the user, and since
* these errors can only occur in UPB_DEBUG_REFS mode, we immediately fail. */
#define CHECK_OOM(predicate) if (!(predicate)) { assert(predicate); exit(1); }
* these errors can only occur in UPB_DEBUG_REFS mode, we use an allocator that
* immediately aborts on failure (avoiding the global allocator, which might
* inject failures). */
#include <stdlib.h>
static void *upb_debugrefs_allocfunc(upb_alloc *alloc, void *ptr,
size_t oldsize, size_t size) {
UPB_UNUSED(alloc);
UPB_UNUSED(oldsize);
if (size == 0) {
free(ptr);
return NULL;
} else {
void *ret = realloc(ptr, size);
if (!ret) {
abort();
}
return ret;
}
}
upb_alloc upb_alloc_debugrefs = {&upb_debugrefs_allocfunc};
typedef struct {
int count; /* How many refs there are (duplicates only allowed for ref2). */
@ -103,8 +125,7 @@ typedef struct {
} trackedref;
static trackedref *trackedref_new(bool is_ref2) {
trackedref *ret = malloc(sizeof(*ret));
CHECK_OOM(ret);
trackedref *ret = upb_malloc(&upb_alloc_debugrefs, sizeof(*ret));
ret->count = 1;
ret->is_ref2 = is_ref2;
return ret;
@ -129,15 +150,15 @@ static void track(const upb_refcounted *r, const void *owner, bool ref2) {
ref->count++;
} else {
trackedref *ref = trackedref_new(ref2);
bool ok = upb_inttable_insertptr(r->refs, owner, upb_value_ptr(ref));
CHECK_OOM(ok);
upb_inttable_insertptr2(r->refs, owner, upb_value_ptr(ref),
&upb_alloc_debugrefs);
if (ref2) {
/* We know this cast is safe when it is a ref2, because it's coming from
* another refcounted object. */
const upb_refcounted *from = owner;
assert(!upb_inttable_lookupptr(from->ref2s, r, NULL));
ok = upb_inttable_insertptr(from->ref2s, r, upb_value_ptr(NULL));
CHECK_OOM(ok);
upb_inttable_insertptr2(from->ref2s, r, upb_value_ptr(NULL),
&upb_alloc_debugrefs);
}
}
upb_unlock();
@ -195,7 +216,6 @@ static void getref2s(const upb_refcounted *owner, upb_inttable *tab) {
upb_value v;
upb_value count;
trackedref *ref;
bool ok;
bool found;
upb_refcounted *to = (upb_refcounted*)upb_inttable_iter_key(&i);
@ -206,8 +226,7 @@ static void getref2s(const upb_refcounted *owner, upb_inttable *tab) {
ref = upb_value_getptr(v);
count = upb_value_int32(ref->count);
ok = upb_inttable_insertptr(tab, to, count);
CHECK_OOM(ok);
upb_inttable_insertptr2(tab, to, count, &upb_alloc_debugrefs);
}
upb_unlock();
}
@ -233,21 +252,19 @@ static void visit_check(const upb_refcounted *obj, const upb_refcounted *subobj,
assert(removed);
newcount = upb_value_getint32(v) - 1;
if (newcount > 0) {
upb_inttable_insert(ref2, (uintptr_t)subobj, upb_value_int32(newcount));
upb_inttable_insert2(ref2, (uintptr_t)subobj, upb_value_int32(newcount),
&upb_alloc_debugrefs);
}
}
static void visit(const upb_refcounted *r, upb_refcounted_visit *v,
void *closure) {
bool ok;
/* In DEBUG_REFS mode we know what existing ref2 refs there are, so we know
* exactly the set of nodes that visit() should visit. So we verify visit()'s
* correctness here. */
check_state state;
state.obj = r;
ok = upb_inttable_init(&state.ref2, UPB_CTYPE_INT32);
CHECK_OOM(ok);
upb_inttable_init2(&state.ref2, UPB_CTYPE_INT32, &upb_alloc_debugrefs);
getref2s(r, &state.ref2);
/* This should visit any children in the ref2 table. */
@ -255,32 +272,22 @@ static void visit(const upb_refcounted *r, upb_refcounted_visit *v,
/* This assertion will fail if the visit() function missed any children. */
assert(upb_inttable_count(&state.ref2) == 0);
upb_inttable_uninit(&state.ref2);
upb_inttable_uninit2(&state.ref2, &upb_alloc_debugrefs);
if (r->vtbl->visit) r->vtbl->visit(r, v, closure);
}
static bool trackinit(upb_refcounted *r) {
r->refs = malloc(sizeof(*r->refs));
r->ref2s = malloc(sizeof(*r->ref2s));
if (!r->refs || !r->ref2s) goto err1;
if (!upb_inttable_init(r->refs, UPB_CTYPE_PTR)) goto err1;
if (!upb_inttable_init(r->ref2s, UPB_CTYPE_PTR)) goto err2;
return true;
err2:
upb_inttable_uninit(r->refs);
err1:
free(r->refs);
free(r->ref2s);
return false;
static void trackinit(upb_refcounted *r) {
r->refs = upb_malloc(&upb_alloc_debugrefs, sizeof(*r->refs));
r->ref2s = upb_malloc(&upb_alloc_debugrefs, sizeof(*r->ref2s));
upb_inttable_init2(r->refs, UPB_CTYPE_PTR, &upb_alloc_debugrefs);
upb_inttable_init2(r->ref2s, UPB_CTYPE_PTR, &upb_alloc_debugrefs);
}
static void trackfree(const upb_refcounted *r) {
upb_inttable_uninit(r->refs);
upb_inttable_uninit(r->ref2s);
free(r->refs);
free(r->ref2s);
upb_inttable_uninit2(r->refs, &upb_alloc_debugrefs);
upb_inttable_uninit2(r->ref2s, &upb_alloc_debugrefs);
upb_free(&upb_alloc_debugrefs, r->refs);
upb_free(&upb_alloc_debugrefs, r->ref2s);
}
#else
@ -303,9 +310,8 @@ static void checkref(const upb_refcounted *r, const void *owner, bool ref2) {
UPB_UNUSED(ref2);
}
static bool trackinit(upb_refcounted *r) {
static void trackinit(upb_refcounted *r) {
UPB_UNUSED(r);
return true;
}
static void trackfree(const upb_refcounted *r) {
@ -415,12 +421,12 @@ static upb_refcounted *pop(tarjan *t) {
}
static void tarjan_newgroup(tarjan *t) {
uint32_t *group = malloc(sizeof(*group));
uint32_t *group = upb_gmalloc(sizeof(*group));
if (!group) oom(t);
/* Push group and empty group leader (we'll fill in leader later). */
if (!upb_inttable_push(&t->groups, upb_value_ptr(group)) ||
!upb_inttable_push(&t->groups, upb_value_ptr(NULL))) {
free(group);
upb_gfree(group);
oom(t);
}
*group = 0;
@ -595,7 +601,7 @@ static bool freeze(upb_refcounted *const*roots, int n, upb_status *s,
if (obj == move) {
/* Removing the last object from a group. */
assert(*obj->group == obj->individual_count);
free(obj->group);
upb_gfree(obj->group);
} else {
obj->next = move->next;
/* This may decrease to zero; we'll collect GRAY objects (if any) that
@ -651,7 +657,7 @@ static bool freeze(upb_refcounted *const*roots, int n, upb_status *s,
/* We eagerly free() the group's count (since we can't easily determine
* the group's remaining size it's the easiest way to ensure it gets
* done). */
free(obj->group);
upb_gfree(obj->group);
/* Visit to release ref2's (done in a separate pass since release_ref2
* depends on o->group being unmodified so it can test merged()). */
@ -671,7 +677,7 @@ err4:
if (!ret) {
upb_inttable_begin(&iter, &t.groups);
for(; !upb_inttable_done(&iter); upb_inttable_next(&iter))
free(upb_value_getptr(upb_inttable_iter_value(&iter)));
upb_gfree(upb_value_getptr(upb_inttable_iter_value(&iter)));
}
upb_inttable_uninit(&t.groups);
err3:
@ -695,7 +701,7 @@ static void merge(upb_refcounted *r, upb_refcounted *from) {
if (merged(r, from)) return;
*r->group += *from->group;
free(from->group);
upb_gfree(from->group);
base = from;
/* Set all refcount pointers in the "from" chain to the merged refcount.
@ -729,7 +735,7 @@ static void unref(const upb_refcounted *r) {
if (unrefgroup(r->group)) {
const upb_refcounted *o;
free(r->group);
upb_gfree(r->group);
/* In two passes, since release_ref2 needs a guarantee that any subobjs
* are alive. */
@ -773,13 +779,10 @@ bool upb_refcounted_init(upb_refcounted *r,
r->vtbl = vtbl;
r->individual_count = 0;
r->is_frozen = false;
r->group = malloc(sizeof(*r->group));
r->group = upb_gmalloc(sizeof(*r->group));
if (!r->group) return false;
*r->group = 0;
if (!trackinit(r)) {
free(r->group);
return false;
}
trackinit(r);
upb_refcounted_ref(r, owner);
return true;
}

@ -31,7 +31,10 @@
/* #define UPB_DEBUG_REFS */
#ifdef __cplusplus
namespace upb { class RefCounted; }
namespace upb {
class RefCounted;
template <class T> class reffed_ptr;
}
#endif
UPB_DECLARE_TYPE(upb::RefCounted, upb_refcounted)
@ -99,6 +102,7 @@ struct upb_refcounted {
};
#ifdef UPB_DEBUG_REFS
extern upb_alloc upb_alloc_debugrefs;
#define UPB_REFCOUNT_INIT(refs, ref2s) \
{&static_refcount, NULL, NULL, 0, true, refs, ref2s}
#else
@ -235,4 +239,109 @@ inline void RefCounted::CheckRef(const void *owner) const {
} /* namespace upb */
#endif
/* upb::reffed_ptr ************************************************************/
#ifdef __cplusplus
#include <algorithm> /* For std::swap(). */
/* Provides RAII semantics for upb refcounted objects. Each reffed_ptr owns a
* ref on whatever object it points to (if any). */
template <class T> class upb::reffed_ptr {
public:
reffed_ptr() : ptr_(NULL) {}
/* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */
template <class U>
reffed_ptr(U* val, const void* ref_donor = NULL)
: ptr_(upb::upcast(val)) {
if (ref_donor) {
assert(ptr_);
ptr_->DonateRef(ref_donor, this);
} else if (ptr_) {
ptr_->Ref(this);
}
}
template <class U>
reffed_ptr(const reffed_ptr<U>& other)
: ptr_(upb::upcast(other.get())) {
if (ptr_) ptr_->Ref(this);
}
reffed_ptr(const reffed_ptr& other)
: ptr_(upb::upcast(other.get())) {
if (ptr_) ptr_->Ref(this);
}
~reffed_ptr() { if (ptr_) ptr_->Unref(this); }
template <class U>
reffed_ptr& operator=(const reffed_ptr<U>& other) {
reset(other.get());
return *this;
}
reffed_ptr& operator=(const reffed_ptr& other) {
reset(other.get());
return *this;
}
/* TODO(haberman): add C++11 move construction/assignment for greater
* efficiency. */
void swap(reffed_ptr& other) {
if (ptr_ == other.ptr_) {
return;
}
if (ptr_) ptr_->DonateRef(this, &other);
if (other.ptr_) other.ptr_->DonateRef(&other, this);
std::swap(ptr_, other.ptr_);
}
T& operator*() const {
assert(ptr_);
return *ptr_;
}
T* operator->() const {
assert(ptr_);
return ptr_;
}
T* get() const { return ptr_; }
/* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */
template <class U>
void reset(U* ptr = NULL, const void* ref_donor = NULL) {
reffed_ptr(ptr, ref_donor).swap(*this);
}
template <class U>
reffed_ptr<U> down_cast() {
return reffed_ptr<U>(upb::down_cast<U*>(get()));
}
template <class U>
reffed_ptr<U> dyn_cast() {
return reffed_ptr<U>(upb::dyn_cast<U*>(get()));
}
/* Plain release() is unsafe; if we were the only owner, it would leak the
* object. Instead we provide this: */
T* ReleaseTo(const void* new_owner) {
T* ret = NULL;
ptr_->DonateRef(this, new_owner);
std::swap(ret, ptr_);
return ret;
}
private:
T* ptr_;
};
#endif /* __cplusplus */
#endif /* UPB_REFCOUNT_H_ */

@ -1,8 +1,6 @@
#include "upb/shim/shim.h"
#include <stdlib.h>
/* Fallback implementation if the shim is not specialized by the JIT. */
#define SHIM_WRITER(type, ctype) \
bool upb_shim_set ## type (void *c, const void *hd, ctype val) { \
@ -28,14 +26,14 @@ bool upb_shim_set(upb_handlers *h, const upb_fielddef *f, size_t offset,
upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
bool ok;
upb_shim_data *d = malloc(sizeof(*d));
upb_shim_data *d = upb_gmalloc(sizeof(*d));
if (!d) return false;
d->offset = offset;
d->hasbit = hasbit;
upb_handlerattr_sethandlerdata(&attr, d);
upb_handlerattr_setalwaysok(&attr, true);
upb_handlers_addcleanup(h, d, free);
upb_handlers_addcleanup(h, d, upb_gfree);
#define TYPE(u, l) \
case UPB_TYPE_##u: \

@ -2,7 +2,6 @@
#include "upb/structdefs.int.h"
#include "upb/symtab.h"
#include <stdlib.h>
#include <string.h>
static void upb_symtab_free(upb_refcounted *r) {
@ -14,13 +13,17 @@ static void upb_symtab_free(upb_refcounted *r) {
upb_def_unref(def, s);
}
upb_strtable_uninit(&s->symtab);
free(s);
upb_gfree(s);
}
upb_symtab *upb_symtab_new(const void *owner) {
static const struct upb_refcounted_vtbl vtbl = {NULL, &upb_symtab_free};
upb_symtab *s = malloc(sizeof(*s));
upb_symtab *s = upb_gmalloc(sizeof(*s));
if (!s) {
return NULL;
}
upb_refcounted_init(upb_symtab_upcast_mutable(s), &vtbl, owner);
upb_strtable_init(&s->symtab, UPB_CTYPE_PTR);
return s;
@ -207,6 +210,10 @@ static bool symtab_add(upb_symtab *s, upb_def *const*defs, size_t n,
upb_strtable addtab;
upb_inttable seen;
if (n == 0 && !freeze_also) {
return true;
}
assert(!upb_symtab_isfrozen(s));
if (!upb_strtable_init(&addtab, UPB_CTYPE_PTR)) {
upb_status_seterrmsg(status, "out of memory");
@ -355,7 +362,7 @@ static bool symtab_add(upb_symtab *s, upb_def *const*defs, size_t n,
add_objs_size++;
}
add_defs = malloc(sizeof(void*) * add_objs_size);
add_defs = upb_gmalloc(sizeof(void*) * add_objs_size);
if (add_defs == NULL) goto oom_err;
upb_strtable_begin(&iter, &addtab);
for (add_n = 0; !upb_strtable_done(&iter); upb_strtable_next(&iter)) {
@ -400,7 +407,7 @@ static bool symtab_add(upb_symtab *s, upb_def *const*defs, size_t n,
success = upb_strtable_insert(&s->symtab, name, upb_value_ptr(def));
UPB_ASSERT_VAR(success, success == true);
}
free(add_objs);
upb_gfree(add_defs);
return true;
oom_err:
@ -421,7 +428,7 @@ err: {
}
}
upb_strtable_uninit(&addtab);
free(add_objs);
upb_gfree(add_defs);
assert(!upb_ok(status));
return false;
}
@ -438,7 +445,7 @@ bool upb_symtab_addfile(upb_symtab *s, upb_filedef *file, upb_status *status) {
bool ret;
n = upb_filedef_defcount(file);
defs = malloc(sizeof(*defs) * n);
defs = upb_gmalloc(sizeof(*defs) * n);
if (defs == NULL) {
upb_status_seterrmsg(status, "Out of memory");
@ -451,7 +458,7 @@ bool upb_symtab_addfile(upb_symtab *s, upb_filedef *file, upb_status *status) {
ret = symtab_add(s, defs, n, NULL, upb_filedef_upcast_mutable(file), status);
free(defs);
upb_gfree(defs);
return ret;
}

@ -6,7 +6,6 @@
#include "upb/table.int.h"
#include <stdlib.h>
#include <string.h>
#define UPB_MAXARRSIZE 16 /* 64k. */
@ -15,6 +14,17 @@
#define ARRAY_SIZE(x) \
((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
#ifdef NDEBUG
static void upb_check_alloc(upb_table *t, upb_alloc *a) {
UPB_UNUSED(t);
UPB_UNUSED(a);
}
#else
static void upb_check_alloc(upb_table *t, upb_alloc *a) {
assert(t->alloc == a);
}
#endif
static const double MAX_LOAD = 0.85;
/* The minimum utilization of the array part of a mixed hash/array table. This
@ -32,11 +42,11 @@ int log2ceil(uint64_t v) {
return UPB_MIN(UPB_MAXARRSIZE, ret);
}
char *upb_strdup(const char *s) {
return upb_strdup2(s, strlen(s));
char *upb_strdup(const char *s, upb_alloc *a) {
return upb_strdup2(s, strlen(s), a);
}
char *upb_strdup2(const char *s, size_t len) {
char *upb_strdup2(const char *s, size_t len, upb_alloc *a) {
size_t n;
char *p;
@ -45,7 +55,7 @@ char *upb_strdup2(const char *s, size_t len) {
/* Always null-terminate, even if binary data; but don't rely on the input to
* have a null-terminating byte since it may be a raw binary buffer. */
n = len + 1;
p = malloc(n);
p = upb_malloc(a, n);
if (p) {
memcpy(p, s, len);
p[len] = 0;
@ -93,16 +103,20 @@ static bool isfull(upb_table *t) {
}
}
static bool init(upb_table *t, upb_ctype_t ctype, uint8_t size_lg2) {
static bool init(upb_table *t, upb_ctype_t ctype, uint8_t size_lg2,
upb_alloc *a) {
size_t bytes;
t->count = 0;
t->ctype = ctype;
t->size_lg2 = size_lg2;
t->mask = upb_table_size(t) ? upb_table_size(t) - 1 : 0;
#ifndef NDEBUG
t->alloc = a;
#endif
bytes = upb_table_size(t) * sizeof(upb_tabent);
if (bytes > 0) {
t->entries = malloc(bytes);
t->entries = upb_malloc(a, bytes);
if (!t->entries) return false;
memset(mutable_entries(t), 0, bytes);
} else {
@ -111,7 +125,10 @@ static bool init(upb_table *t, upb_ctype_t ctype, uint8_t size_lg2) {
return true;
}
static void uninit(upb_table *t) { free(mutable_entries(t)); }
static void uninit(upb_table *t, upb_alloc *a) {
upb_check_alloc(t, a);
upb_free(a, mutable_entries(t));
}
static upb_tabent *emptyent(upb_table *t) {
upb_tabent *e = mutable_entries(t) + upb_table_size(t);
@ -264,8 +281,8 @@ static size_t begin(const upb_table *t) {
/* A simple "subclass" of upb_table that only adds a hash function for strings. */
static upb_tabkey strcopy(lookupkey_t k2) {
char *str = malloc(k2.str.len + sizeof(uint32_t) + 1);
static upb_tabkey strcopy(lookupkey_t k2, upb_alloc *a) {
char *str = upb_malloc(a, k2.str.len + sizeof(uint32_t) + 1);
if (str == NULL) return 0;
memcpy(str, &k2.str.len, sizeof(uint32_t));
memcpy(str + sizeof(uint32_t), k2.str.str, k2.str.len + 1);
@ -284,51 +301,56 @@ static bool streql(upb_tabkey k1, lookupkey_t k2) {
return len == k2.str.len && memcmp(str, k2.str.str, len) == 0;
}
bool upb_strtable_init(upb_strtable *t, upb_ctype_t ctype) {
return init(&t->t, ctype, 2);
bool upb_strtable_init2(upb_strtable *t, upb_ctype_t ctype, upb_alloc *a) {
return init(&t->t, ctype, 2, a);
}
void upb_strtable_uninit(upb_strtable *t) {
void upb_strtable_uninit2(upb_strtable *t, upb_alloc *a) {
size_t i;
for (i = 0; i < upb_table_size(&t->t); i++)
free((void*)t->t.entries[i].key);
uninit(&t->t);
upb_free(a, (void*)t->t.entries[i].key);
uninit(&t->t, a);
}
bool upb_strtable_resize(upb_strtable *t, size_t size_lg2) {
bool upb_strtable_resize(upb_strtable *t, size_t size_lg2, upb_alloc *a) {
upb_strtable new_table;
upb_strtable_iter i;
if (!init(&new_table.t, t->t.ctype, size_lg2))
upb_check_alloc(&t->t, a);
if (!init(&new_table.t, t->t.ctype, size_lg2, a))
return false;
upb_strtable_begin(&i, t);
for ( ; !upb_strtable_done(&i); upb_strtable_next(&i)) {
upb_strtable_insert2(
upb_strtable_insert3(
&new_table,
upb_strtable_iter_key(&i),
upb_strtable_iter_keylength(&i),
upb_strtable_iter_value(&i));
upb_strtable_iter_value(&i),
a);
}
upb_strtable_uninit(t);
upb_strtable_uninit2(t, a);
*t = new_table;
return true;
}
bool upb_strtable_insert2(upb_strtable *t, const char *k, size_t len,
upb_value v) {
bool upb_strtable_insert3(upb_strtable *t, const char *k, size_t len,
upb_value v, upb_alloc *a) {
lookupkey_t key;
upb_tabkey tabkey;
uint32_t hash;
upb_check_alloc(&t->t, a);
if (isfull(&t->t)) {
/* Need to resize. New table of double the size, add old elements to it. */
if (!upb_strtable_resize(t, t->t.size_lg2 + 1)) {
if (!upb_strtable_resize(t, t->t.size_lg2 + 1, a)) {
return false;
}
}
key = strkey2(k, len);
tabkey = strcopy(key);
tabkey = strcopy(key, a);
if (tabkey == 0) return false;
hash = MurmurHash2(key.str.str, key.str.len, 0);
@ -342,12 +364,12 @@ bool upb_strtable_lookup2(const upb_strtable *t, const char *key, size_t len,
return lookup(&t->t, strkey2(key, len), v, hash, &streql);
}
bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len,
upb_value *val) {
bool upb_strtable_remove3(upb_strtable *t, const char *key, size_t len,
upb_value *val, upb_alloc *alloc) {
uint32_t hash = MurmurHash2(key, strlen(key), 0);
upb_tabkey tabkey;
if (rm(&t->t, strkey2(key, len), val, &tabkey, hash, &streql)) {
free((void*)tabkey);
upb_free(alloc, (void*)tabkey);
return true;
} else {
return false;
@ -374,12 +396,12 @@ bool upb_strtable_done(const upb_strtable_iter *i) {
upb_tabent_isempty(str_tabent(i));
}
const char *upb_strtable_iter_key(upb_strtable_iter *i) {
const char *upb_strtable_iter_key(const upb_strtable_iter *i) {
assert(!upb_strtable_done(i));
return upb_tabstr(str_tabent(i)->key, NULL);
}
size_t upb_strtable_iter_keylength(upb_strtable_iter *i) {
size_t upb_strtable_iter_keylength(const upb_strtable_iter *i) {
uint32_t len;
assert(!upb_strtable_done(i));
upb_tabstr(str_tabent(i)->key, &len);
@ -454,18 +476,18 @@ static void check(upb_inttable *t) {
}
bool upb_inttable_sizedinit(upb_inttable *t, upb_ctype_t ctype,
size_t asize, int hsize_lg2) {
size_t asize, int hsize_lg2, upb_alloc *a) {
size_t array_bytes;
if (!init(&t->t, ctype, hsize_lg2)) return false;
if (!init(&t->t, ctype, hsize_lg2, a)) return false;
/* Always make the array part at least 1 long, so that we know key 0
* won't be in the hash part, which simplifies things. */
t->array_size = UPB_MAX(1, asize);
t->array_count = 0;
array_bytes = t->array_size * sizeof(upb_value);
t->array = malloc(array_bytes);
t->array = upb_malloc(a, array_bytes);
if (!t->array) {
uninit(&t->t);
uninit(&t->t, a);
return false;
}
memset(mutable_array(t), 0xff, array_bytes);
@ -473,22 +495,23 @@ bool upb_inttable_sizedinit(upb_inttable *t, upb_ctype_t ctype,
return true;
}
bool upb_inttable_init(upb_inttable *t, upb_ctype_t ctype) {
return upb_inttable_sizedinit(t, ctype, 0, 4);
bool upb_inttable_init2(upb_inttable *t, upb_ctype_t ctype, upb_alloc *a) {
return upb_inttable_sizedinit(t, ctype, 0, 4, a);
}
void upb_inttable_uninit(upb_inttable *t) {
uninit(&t->t);
free(mutable_array(t));
void upb_inttable_uninit2(upb_inttable *t, upb_alloc *a) {
uninit(&t->t, a);
upb_free(a, mutable_array(t));
}
bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val) {
/* XXX: Table can't store value (uint64_t)-1. Need to somehow statically
* guarantee that this is not necessary, or fix the limitation. */
bool upb_inttable_insert2(upb_inttable *t, uintptr_t key, upb_value val,
upb_alloc *a) {
upb_tabval tabval;
tabval.val = val.val;
UPB_UNUSED(tabval);
assert(upb_arrhas(tabval));
assert(upb_arrhas(tabval)); /* This will reject (uint64_t)-1. Fix this. */
upb_check_alloc(&t->t, a);
if (key < t->array_size) {
assert(!upb_arrhas(t->array[key]));
@ -499,8 +522,11 @@ bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val) {
/* Need to resize the hash part, but we re-use the array part. */
size_t i;
upb_table new_table;
if (!init(&new_table, t->t.ctype, t->t.size_lg2 + 1))
if (!init(&new_table, t->t.ctype, t->t.size_lg2 + 1, a)) {
return false;
}
for (i = begin(&t->t); i < upb_table_size(&t->t); i = next(&t->t, i)) {
const upb_tabent *e = &t->t.entries[i];
uint32_t hash;
@ -513,7 +539,7 @@ bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val) {
assert(t->t.count == new_table.count);
uninit(&t->t);
uninit(&t->t, a);
t->t = new_table;
}
insert(&t->t, intkey(key), key, val, upb_inthash(key), &inthash, &inteql);
@ -559,8 +585,9 @@ bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val) {
return success;
}
bool upb_inttable_push(upb_inttable *t, upb_value val) {
return upb_inttable_insert(t, upb_inttable_count(t), val);
bool upb_inttable_push2(upb_inttable *t, upb_value val, upb_alloc *a) {
upb_check_alloc(&t->t, a);
return upb_inttable_insert2(t, upb_inttable_count(t), val, a);
}
upb_value upb_inttable_pop(upb_inttable *t) {
@ -570,8 +597,10 @@ upb_value upb_inttable_pop(upb_inttable *t) {
return val;
}
bool upb_inttable_insertptr(upb_inttable *t, const void *key, upb_value val) {
return upb_inttable_insert(t, (uintptr_t)key, val);
bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val,
upb_alloc *a) {
upb_check_alloc(&t->t, a);
return upb_inttable_insert2(t, (uintptr_t)key, val, a);
}
bool upb_inttable_lookupptr(const upb_inttable *t, const void *key,
@ -583,7 +612,7 @@ bool upb_inttable_removeptr(upb_inttable *t, const void *key, upb_value *val) {
return upb_inttable_remove(t, (uintptr_t)key, val);
}
void upb_inttable_compact(upb_inttable *t) {
void upb_inttable_compact2(upb_inttable *t, upb_alloc *a) {
/* A power-of-two histogram of the table keys. */
size_t counts[UPB_MAXARRSIZE + 1] = {0};
@ -595,6 +624,8 @@ void upb_inttable_compact(upb_inttable *t) {
int size_lg2;
upb_inttable new_t;
upb_check_alloc(&t->t, a);
upb_inttable_begin(&i, t);
for (; !upb_inttable_done(&i); upb_inttable_next(&i)) {
uintptr_t key = upb_inttable_iter_key(&i);
@ -627,16 +658,16 @@ void upb_inttable_compact(upb_inttable *t) {
size_t hash_size = hash_count ? (hash_count / MAX_LOAD) + 1 : 0;
size_t hashsize_lg2 = log2ceil(hash_size);
upb_inttable_sizedinit(&new_t, t->t.ctype, arr_size, hashsize_lg2);
upb_inttable_sizedinit(&new_t, t->t.ctype, arr_size, hashsize_lg2, a);
upb_inttable_begin(&i, t);
for (; !upb_inttable_done(&i); upb_inttable_next(&i)) {
uintptr_t k = upb_inttable_iter_key(&i);
upb_inttable_insert(&new_t, k, upb_inttable_iter_value(&i));
upb_inttable_insert2(&new_t, k, upb_inttable_iter_value(&i), a);
}
assert(new_t.array_size == arr_size);
assert(new_t.t.size_lg2 == hashsize_lg2);
}
upb_inttable_uninit(t);
upb_inttable_uninit2(t, a);
*t = new_t;
}

@ -63,10 +63,14 @@ typedef struct {
#endif
/* Like strdup(), which isn't always available since it's not ANSI C. */
char *upb_strdup(const char *s);
char *upb_strdup(const char *s, upb_alloc *a);
/* Variant that works with a length-delimited rather than NULL-delimited string,
* as supported by strtable. */
char *upb_strdup2(const char *s, size_t len);
char *upb_strdup2(const char *s, size_t len, upb_alloc *a);
UPB_INLINE char *upb_gstrdup(const char *s) {
return upb_strdup(s, &upb_alloc_global);
}
UPB_INLINE void _upb_value_setval(upb_value *v, uint64_t val,
upb_ctype_t ctype) {
@ -243,14 +247,35 @@ typedef struct {
* initialize const hash tables. Then we cast away const when we have to.
*/
const upb_tabent *entries;
#ifndef NDEBUG
/* This table's allocator. We make the user pass it in to every relevant
* function and only use this to check it in debug mode. We do this solely
* to keep upb_table as small as possible. This might seem slightly paranoid
* but the plan is to use upb_table for all map fields and extension sets in
* a forthcoming message representation, so there could be a lot of these.
* If this turns out to be too annoying later, we can change it (since this
* is an internal-only header file). */
upb_alloc *alloc;
#endif
} upb_table;
#ifdef NDEBUG
#define UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries) \
{count, mask, ctype, size_lg2, entries}
#else
/* At the moment the only mutable tables we statically initialize are debug
* ref tables. */
#define UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries) \
{count, mask, ctype, size_lg2, entries, &upb_alloc_debugrefs}
#endif
typedef struct {
upb_table t;
} upb_strtable;
#define UPB_STRTABLE_INIT(count, mask, ctype, size_lg2, entries) \
{{count, mask, ctype, size_lg2, entries}}
{UPB_TABLE_INIT(count, mask, ctype, size_lg2, entries)}
#define UPB_EMPTY_STRTABLE_INIT(ctype) \
UPB_STRTABLE_INIT(0, 0, ctype, 0, NULL)
@ -263,7 +288,7 @@ typedef struct {
} upb_inttable;
#define UPB_INTTABLE_INIT(count, mask, ctype, size_lg2, ent, a, asize, acount) \
{{count, mask, ctype, size_lg2, ent}, a, asize, acount}
{UPB_TABLE_INIT(count, mask, ctype, size_lg2, ent), a, asize, acount}
#define UPB_EMPTY_INTTABLE_INIT(ctype) \
UPB_INTTABLE_INIT(0, 0, ctype, 0, NULL, NULL, 0, 0)
@ -303,10 +328,26 @@ UPB_INLINE bool upb_arrhas(upb_tabval key) {
/* Initialize and uninitialize a table, respectively. If memory allocation
* failed, false is returned that the table is uninitialized. */
bool upb_inttable_init(upb_inttable *table, upb_ctype_t ctype);
bool upb_strtable_init(upb_strtable *table, upb_ctype_t ctype);
void upb_inttable_uninit(upb_inttable *table);
void upb_strtable_uninit(upb_strtable *table);
bool upb_inttable_init2(upb_inttable *table, upb_ctype_t ctype, upb_alloc *a);
bool upb_strtable_init2(upb_strtable *table, upb_ctype_t ctype, upb_alloc *a);
void upb_inttable_uninit2(upb_inttable *table, upb_alloc *a);
void upb_strtable_uninit2(upb_strtable *table, upb_alloc *a);
UPB_INLINE bool upb_inttable_init(upb_inttable *table, upb_ctype_t ctype) {
return upb_inttable_init2(table, ctype, &upb_alloc_global);
}
UPB_INLINE bool upb_strtable_init(upb_strtable *table, upb_ctype_t ctype) {
return upb_strtable_init2(table, ctype, &upb_alloc_global);
}
UPB_INLINE void upb_inttable_uninit(upb_inttable *table) {
upb_inttable_uninit2(table, &upb_alloc_global);
}
UPB_INLINE void upb_strtable_uninit(upb_strtable *table) {
upb_strtable_uninit2(table, &upb_alloc_global);
}
/* Returns the number of values in the table. */
size_t upb_inttable_count(const upb_inttable *t);
@ -321,9 +362,20 @@ UPB_INLINE size_t upb_strtable_count(const upb_strtable *t) {
*
* If a table resize was required but memory allocation failed, false is
* returned and the table is unchanged. */
bool upb_inttable_insert(upb_inttable *t, uintptr_t key, upb_value val);
bool upb_strtable_insert2(upb_strtable *t, const char *key, size_t len,
upb_value val);
bool upb_inttable_insert2(upb_inttable *t, uintptr_t key, upb_value val,
upb_alloc *a);
bool upb_strtable_insert3(upb_strtable *t, const char *key, size_t len,
upb_value val, upb_alloc *a);
UPB_INLINE bool upb_inttable_insert(upb_inttable *t, uintptr_t key,
upb_value val) {
return upb_inttable_insert2(t, key, val, &upb_alloc_global);
}
UPB_INLINE bool upb_strtable_insert2(upb_strtable *t, const char *key,
size_t len, upb_value val) {
return upb_strtable_insert3(t, key, len, val, &upb_alloc_global);
}
/* For NULL-terminated strings. */
UPB_INLINE bool upb_strtable_insert(upb_strtable *t, const char *key,
@ -346,8 +398,13 @@ UPB_INLINE bool upb_strtable_lookup(const upb_strtable *t, const char *key,
/* Removes an item from the table. Returns true if the remove was successful,
* and stores the removed item in *val if non-NULL. */
bool upb_inttable_remove(upb_inttable *t, uintptr_t key, upb_value *val);
bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len,
upb_value *val);
bool upb_strtable_remove3(upb_strtable *t, const char *key, size_t len,
upb_value *val, upb_alloc *alloc);
UPB_INLINE bool upb_strtable_remove2(upb_strtable *t, const char *key,
size_t len, upb_value *val) {
return upb_strtable_remove3(t, key, len, val, &upb_alloc_global);
}
/* For NULL-terminated strings. */
UPB_INLINE bool upb_strtable_remove(upb_strtable *t, const char *key,
@ -362,19 +419,33 @@ bool upb_inttable_replace(upb_inttable *t, uintptr_t key, upb_value val);
/* Handy routines for treating an inttable like a stack. May not be mixed with
* other insert/remove calls. */
bool upb_inttable_push(upb_inttable *t, upb_value val);
bool upb_inttable_push2(upb_inttable *t, upb_value val, upb_alloc *a);
upb_value upb_inttable_pop(upb_inttable *t);
UPB_INLINE bool upb_inttable_push(upb_inttable *t, upb_value val) {
return upb_inttable_push2(t, val, &upb_alloc_global);
}
/* Convenience routines for inttables with pointer keys. */
bool upb_inttable_insertptr(upb_inttable *t, const void *key, upb_value val);
bool upb_inttable_insertptr2(upb_inttable *t, const void *key, upb_value val,
upb_alloc *a);
bool upb_inttable_removeptr(upb_inttable *t, const void *key, upb_value *val);
bool upb_inttable_lookupptr(
const upb_inttable *t, const void *key, upb_value *val);
UPB_INLINE bool upb_inttable_insertptr(upb_inttable *t, const void *key,
upb_value val) {
return upb_inttable_insertptr2(t, key, val, &upb_alloc_global);
}
/* Optimizes the table for the current set of entries, for both memory use and
* lookup time. Client should call this after all entries have been inserted;
* inserting more entries is legal, but will likely require a table resize. */
void upb_inttable_compact(upb_inttable *t);
void upb_inttable_compact2(upb_inttable *t, upb_alloc *a);
UPB_INLINE void upb_inttable_compact(upb_inttable *t) {
upb_inttable_compact2(t, &upb_alloc_global);
}
/* A special-case inlinable version of the lookup routine for 32-bit
* integers. */
@ -403,7 +474,7 @@ UPB_INLINE bool upb_inttable_lookup32(const upb_inttable *t, uint32_t key,
}
/* Exposed for testing only. */
bool upb_strtable_resize(upb_strtable *t, size_t size_lg2);
bool upb_strtable_resize(upb_strtable *t, size_t size_lg2, upb_alloc *a);
/* Iterators ******************************************************************/
@ -448,8 +519,8 @@ typedef struct {
void upb_strtable_begin(upb_strtable_iter *i, const upb_strtable *t);
void upb_strtable_next(upb_strtable_iter *i);
bool upb_strtable_done(const upb_strtable_iter *i);
const char *upb_strtable_iter_key(upb_strtable_iter *i);
size_t upb_strtable_iter_keylength(upb_strtable_iter *i);
const char *upb_strtable_iter_key(const upb_strtable_iter *i);
size_t upb_strtable_iter_keylength(const upb_strtable_iter *i);
upb_value upb_strtable_iter_value(const upb_strtable_iter *i);
void upb_strtable_iter_setdone(upb_strtable_iter *i);
bool upb_strtable_iter_isequal(const upb_strtable_iter *i1,

@ -25,6 +25,19 @@ static void nullz(upb_status *status) {
memcpy(status->msg + sizeof(status->msg) - len, ellipsis, len);
}
/* upb_upberr *****************************************************************/
upb_errorspace upb_upberr = {"upb error"};
void upb_upberr_setoom(upb_status *status) {
status->error_space_ = &upb_upberr;
upb_status_seterrmsg(status, "Out of memory");
}
/* upb_status *****************************************************************/
void upb_status_clear(upb_status *status) {
if (!status) return;
status->ok_ = true;
@ -63,16 +76,245 @@ void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args) {
nullz(status);
}
void upb_status_seterrcode(upb_status *status, upb_errorspace *space,
int code) {
if (!status) return;
status->ok_ = false;
status->error_space_ = space;
status->code_ = code;
space->set_message(status, code);
}
void upb_status_copy(upb_status *to, const upb_status *from) {
if (!to) return;
*to = *from;
}
/* upb_alloc ******************************************************************/
static void *upb_global_allocfunc(upb_alloc *alloc, void *ptr, size_t oldsize,
size_t size) {
UPB_UNUSED(alloc);
UPB_UNUSED(oldsize);
if (size == 0) {
free(ptr);
return NULL;
} else {
return realloc(ptr, size);
}
}
upb_alloc upb_alloc_global = {&upb_global_allocfunc};
/* upb_arena ******************************************************************/
/* Be conservative and choose 16 in case anyone is using SSE. */
static const size_t maxalign = 16;
static size_t align_up(size_t size) {
return ((size + maxalign - 1) / maxalign) * maxalign;
}
typedef struct mem_block {
struct mem_block *next;
size_t size;
size_t used;
bool owned;
/* Data follows. */
} mem_block;
typedef struct cleanup_ent {
struct cleanup_ent *next;
upb_cleanup_func *cleanup;
void *ud;
} cleanup_ent;
static void upb_arena_addblock(upb_arena *a, void *ptr, size_t size,
bool owned) {
mem_block *block = ptr;
block->next = a->block_head;
block->size = size;
block->used = align_up(sizeof(mem_block));
block->owned = owned;
a->block_head = block;
/* TODO(haberman): ASAN poison. */
}
static mem_block *upb_arena_allocblock(upb_arena *a, size_t size) {
size_t block_size = UPB_MAX(size, a->next_block_size) + sizeof(mem_block);
mem_block *block = upb_malloc(a->block_alloc, block_size);
if (!block) {
return NULL;
}
upb_arena_addblock(a, block, block_size, true);
a->next_block_size = UPB_MIN(block_size * 2, a->max_block_size);
return block;
}
static void *upb_arena_doalloc(upb_alloc *alloc, void *ptr, size_t oldsize,
size_t size) {
upb_arena *a = (upb_arena*)alloc; /* upb_alloc is initial member. */
mem_block *block = a->block_head;
void *ret;
if (size == 0) {
return NULL; /* We are an arena, don't need individual frees. */
}
size = align_up(size);
/* TODO(haberman): special-case if this is a realloc of the last alloc? */
if (!block || block->size - block->used < size) {
/* Slow path: have to allocate a new block. */
block = upb_arena_allocblock(a, size);
if (!block) {
return NULL; /* Out of memory. */
}
}
ret = (char*)block + block->used;
block->used += size;
if (oldsize > 0) {
memcpy(ret, ptr, oldsize); /* Preserve existing data. */
}
/* TODO(haberman): ASAN unpoison. */
a->bytes_allocated += size;
return ret;
}
/* Public Arena API ***********************************************************/
void upb_arena_init(upb_arena *a) {
a->alloc.func = &upb_arena_doalloc;
a->block_alloc = &upb_alloc_global;
a->bytes_allocated = 0;
a->next_block_size = 256;
a->max_block_size = 16384;
a->cleanup_head = NULL;
a->block_head = NULL;
}
void upb_arena_init2(upb_arena *a, void *mem, size_t size, upb_alloc *alloc) {
upb_arena_init(a);
if (size > sizeof(mem_block)) {
upb_arena_addblock(a, mem, size, false);
}
if (alloc) {
a->block_alloc = alloc;
}
}
void upb_arena_uninit(upb_arena *a) {
cleanup_ent *ent = a->cleanup_head;
mem_block *block = a->block_head;
while (ent) {
ent->cleanup(ent->ud);
ent = ent->next;
}
/* Must do this after running cleanup functions, because this will delete
* the memory we store our cleanup entries in! */
while (block) {
mem_block *next = block->next;
if (block->owned) {
upb_free(a->block_alloc, block);
}
block = next;
}
}
bool upb_arena_addcleanup(upb_arena *a, upb_cleanup_func *func, void *ud) {
cleanup_ent *ent = upb_malloc(&a->alloc, sizeof(cleanup_ent));
if (!ent) {
return false; /* Out of memory. */
}
ent->cleanup = func;
ent->ud = ud;
ent->next = a->cleanup_head;
a->cleanup_head = ent;
return true;
}
size_t upb_arena_bytesallocated(const upb_arena *a) {
return a->bytes_allocated;
}
/* Standard error functions ***************************************************/
static bool default_err(void *ud, const upb_status *status) {
UPB_UNUSED(ud);
UPB_UNUSED(status);
return false;
}
static bool write_err_to(void *ud, const upb_status *status) {
upb_status *copy_to = ud;
upb_status_copy(copy_to, status);
return false;
}
/* upb_env ********************************************************************/
void upb_env_initonly(upb_env *e) {
e->ok_ = true;
e->error_func_ = &default_err;
e->error_ud_ = NULL;
}
void upb_env_init(upb_env *e) {
upb_arena_init(&e->arena_);
upb_env_initonly(e);
}
void upb_env_uninit(upb_env *e) {
upb_arena_uninit(&e->arena_);
}
void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud) {
e->error_func_ = func;
e->error_ud_ = ud;
}
void upb_env_reporterrorsto(upb_env *e, upb_status *s) {
e->error_func_ = &write_err_to;
e->error_ud_ = s;
}
bool upb_env_reporterror(upb_env *e, const upb_status *status) {
e->ok_ = false;
return e->error_func_(e->error_ud_, status);
}
void *upb_env_malloc(upb_env *e, size_t size) {
return upb_malloc(&e->arena_.alloc, size);
}
void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size) {
return upb_realloc(&e->arena_.alloc, ptr, oldsize, size);
}
void upb_env_free(upb_env *e, void *ptr) {
upb_free(&e->arena_.alloc, ptr);
}
bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud) {
return upb_arena_addcleanup(&e->arena_, func, ud);
}
size_t upb_env_bytesallocated(const upb_env *e) {
return upb_arena_bytesallocated(&e->arena_);
}

@ -13,6 +13,18 @@
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
namespace upb {
class Allocator;
class Arena;
class Environment;
class ErrorSpace;
class Status;
template <int N> class InlinedArena;
template <int N> class InlinedEnvironment;
}
#endif
/* UPB_INLINE: inline if possible, emit standalone code if required. */
#ifdef __cplusplus
#define UPB_INLINE inline
@ -80,6 +92,7 @@
#define UPB_ASSERT_STDLAYOUT(type) \
static_assert(std::is_standard_layout<type>::value, \
#type " must be standard layout");
#define UPB_FINAL final
#else /* !defined(UPB_CXX11) */
#define UPB_DISALLOW_COPY_AND_ASSIGN(class_name) \
class_name(const class_name&); \
@ -89,6 +102,7 @@
~class_name(); \
UPB_DISALLOW_COPY_AND_ASSIGN(class_name)
#define UPB_ASSERT_STDLAYOUT(type)
#define UPB_FINAL
#endif
/* UPB_DECLARE_TYPE()
@ -190,6 +204,7 @@
/* Generic function type. */
typedef void upb_func();
/* C++ Casts ******************************************************************/
#ifdef __cplusplus
@ -267,247 +282,428 @@ class PointerBase2 : public PointerBase<T, Base> {
#endif
/* upb::reffed_ptr ************************************************************/
/* upb::ErrorSpace ************************************************************/
/* A upb::ErrorSpace represents some domain of possible error values. This lets
* upb::Status attach specific error codes to operations, like POSIX/C errno,
* Win32 error codes, etc. Clients who want to know the very specific error
* code can check the error space and then know the type of the integer code.
*
* NOTE: upb::ErrorSpace is currently not used and should be considered
* experimental. It is important primarily in cases where upb is performing
* I/O, but upb doesn't currently have any components that do this. */
UPB_DECLARE_TYPE(upb::ErrorSpace, upb_errorspace)
#ifdef __cplusplus
class upb::ErrorSpace {
#else
struct upb_errorspace {
#endif
const char *name;
};
#include <algorithm> /* For std::swap(). */
namespace upb {
/* upb::Status ****************************************************************/
/* upb::Status represents a success or failure status and error message.
* It owns no resources and allocates no memory, so it should work
* even in OOM situations. */
UPB_DECLARE_TYPE(upb::Status, upb_status)
/* The maximum length of an error message before it will get truncated. */
#define UPB_STATUS_MAX_MESSAGE 128
UPB_BEGIN_EXTERN_C
/* Provides RAII semantics for upb refcounted objects. Each reffed_ptr owns a
* ref on whatever object it points to (if any). */
template <class T> class reffed_ptr {
const char *upb_status_errmsg(const upb_status *status);
bool upb_ok(const upb_status *status);
upb_errorspace *upb_status_errspace(const upb_status *status);
int upb_status_errcode(const upb_status *status);
/* Any of the functions that write to a status object allow status to be NULL,
* to support use cases where the function's caller does not care about the
* status message. */
void upb_status_clear(upb_status *status);
void upb_status_seterrmsg(upb_status *status, const char *msg);
void upb_status_seterrf(upb_status *status, const char *fmt, ...);
void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args);
void upb_status_copy(upb_status *to, const upb_status *from);
UPB_END_EXTERN_C
#ifdef __cplusplus
class upb::Status {
public:
reffed_ptr() : ptr_(NULL) {}
/* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */
template <class U>
reffed_ptr(U* val, const void* ref_donor = NULL)
: ptr_(upb::upcast(val)) {
if (ref_donor) {
assert(ptr_);
ptr_->DonateRef(ref_donor, this);
} else if (ptr_) {
ptr_->Ref(this);
}
}
Status() { upb_status_clear(this); }
template <class U>
reffed_ptr(const reffed_ptr<U>& other)
: ptr_(upb::upcast(other.get())) {
if (ptr_) ptr_->Ref(this);
}
/* Returns true if there is no error. */
bool ok() const { return upb_ok(this); }
reffed_ptr(const reffed_ptr& other)
: ptr_(upb::upcast(other.get())) {
if (ptr_) ptr_->Ref(this);
}
/* Optional error space and code, useful if the caller wants to
* programmatically check the specific kind of error. */
ErrorSpace* error_space() { return upb_status_errspace(this); }
int error_code() const { return upb_status_errcode(this); }
~reffed_ptr() { if (ptr_) ptr_->Unref(this); }
/* The returned string is invalidated by any other call into the status. */
const char *error_message() const { return upb_status_errmsg(this); }
template <class U>
reffed_ptr& operator=(const reffed_ptr<U>& other) {
reset(other.get());
return *this;
/* The error message will be truncated if it is longer than
* UPB_STATUS_MAX_MESSAGE-4. */
void SetErrorMessage(const char* msg) { upb_status_seterrmsg(this, msg); }
void SetFormattedErrorMessage(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
upb_status_vseterrf(this, fmt, args);
va_end(args);
}
reffed_ptr& operator=(const reffed_ptr& other) {
reset(other.get());
return *this;
}
/* Resets the status to a successful state with no message. */
void Clear() { upb_status_clear(this); }
/* TODO(haberman): add C++11 move construction/assignment for greater
* efficiency. */
void CopyFrom(const Status& other) { upb_status_copy(this, &other); }
void swap(reffed_ptr& other) {
if (ptr_ == other.ptr_) {
return;
}
private:
UPB_DISALLOW_COPY_AND_ASSIGN(Status)
#else
struct upb_status {
#endif
bool ok_;
/* Specific status code defined by some error space (optional). */
int code_;
upb_errorspace *error_space_;
/* TODO(haberman): add file/line of error? */
/* Error message; NULL-terminated. */
char msg[UPB_STATUS_MAX_MESSAGE];
};
#define UPB_STATUS_INIT {true, 0, NULL, {0}}
/** Built-in error spaces. ****************************************************/
/* Errors raised by upb that we want to be able to detect programmatically. */
typedef enum {
UPB_NOMEM /* Can't reuse ENOMEM because it is POSIX, not ISO C. */
} upb_errcode_t;
if (ptr_) ptr_->DonateRef(this, &other);
if (other.ptr_) other.ptr_->DonateRef(&other, this);
std::swap(ptr_, other.ptr_);
extern upb_errorspace upb_upberr;
void upb_upberr_setoom(upb_status *s);
/* Since errno is defined by standard C, we define an error space for it in
* core upb. Other error spaces should be defined in other, platform-specific
* modules. */
extern upb_errorspace upb_errnoerr;
/** upb::Allocator ************************************************************/
/* A upb::Allocator is a possibly-stateful allocator object.
*
* It could either be an arena allocator (which doesn't require individual
* free() calls) or a regular malloc() (which does). The client must therefore
* free memory unless it knows that the allocator is an arena allocator. */
UPB_DECLARE_TYPE(upb::Allocator, upb_alloc)
/* A malloc()/free() function.
* If "size" is 0 then the function acts like free(), otherwise it acts like
* realloc(). Only "oldsize" bytes from a previous allocation are preserved. */
typedef void *upb_alloc_func(upb_alloc *alloc, void *ptr, size_t oldsize,
size_t size);
#ifdef __cplusplus
class upb::Allocator UPB_FINAL {
public:
Allocator() {}
private:
UPB_DISALLOW_COPY_AND_ASSIGN(Allocator)
public:
#else
struct upb_alloc {
#endif /* __cplusplus */
upb_alloc_func *func;
};
UPB_INLINE void *upb_malloc(upb_alloc *alloc, size_t size) {
assert(size > 0);
return alloc->func(alloc, NULL, 0, size);
}
T& operator*() const {
assert(ptr_);
return *ptr_;
UPB_INLINE void *upb_realloc(upb_alloc *alloc, void *ptr, size_t oldsize,
size_t size) {
assert(size > 0);
return alloc->func(alloc, ptr, oldsize, size);
}
T* operator->() const {
assert(ptr_);
return ptr_;
UPB_INLINE void upb_free(upb_alloc *alloc, void *ptr) {
alloc->func(alloc, ptr, 0, 0);
}
T* get() const { return ptr_; }
/* The global allocator used by upb. Uses the standard malloc()/free(). */
/* If ref_donor is NULL, takes a new ref, otherwise adopts from ref_donor. */
template <class U>
void reset(U* ptr = NULL, const void* ref_donor = NULL) {
reffed_ptr(ptr, ref_donor).swap(*this);
}
extern upb_alloc upb_alloc_global;
template <class U>
reffed_ptr<U> down_cast() {
return reffed_ptr<U>(upb::down_cast<U*>(get()));
/* Functions that hard-code the global malloc.
*
* We still get benefit because we can put custom logic into our global
* allocator, like injecting out-of-memory faults in debug/testing builds. */
UPB_INLINE void *upb_gmalloc(size_t size) {
return upb_malloc(&upb_alloc_global, size);
}
template <class U>
reffed_ptr<U> dyn_cast() {
return reffed_ptr<U>(upb::dyn_cast<U*>(get()));
UPB_INLINE void *upb_grealloc(void *ptr, size_t oldsize, size_t size) {
return upb_realloc(&upb_alloc_global, ptr, oldsize, size);
}
/* Plain release() is unsafe; if we were the only owner, it would leak the
* object. Instead we provide this: */
T* ReleaseTo(const void* new_owner) {
T* ret = NULL;
ptr_->DonateRef(this, new_owner);
std::swap(ret, ptr_);
return ret;
UPB_INLINE void upb_gfree(void *ptr) {
upb_free(&upb_alloc_global, ptr);
}
private:
T* ptr_;
};
/* upb::Arena *****************************************************************/
/* upb::Arena is a specific allocator implementation that uses arena allocation.
* The user provides an allocator that will be used to allocate the underlying
* arena blocks. Arenas by nature do not require the individual allocations
* to be freed. However the Arena does allow users to register cleanup
* functions that will run when the arena is destroyed.
*
* A upb::Arena is *not* thread-safe.
*
* You could write a thread-safe arena allocator that satisfies the
* upb::Allocator interface, but it would not be as efficient for the
* single-threaded case. */
UPB_DECLARE_TYPE(upb::Arena, upb_arena)
} /* namespace upb */
typedef void upb_cleanup_func(void *ud);
#endif /* __cplusplus */
#define UPB_ARENA_BLOCK_OVERHEAD (sizeof(size_t)*4)
UPB_BEGIN_EXTERN_C
/* upb::Status ****************************************************************/
void upb_arena_init(upb_arena *a);
void upb_arena_init2(upb_arena *a, void *mem, size_t n, upb_alloc *alloc);
void upb_arena_uninit(upb_arena *a);
upb_alloc *upb_arena_alloc(upb_arena *a);
bool upb_arena_addcleanup(upb_arena *a, upb_cleanup_func *func, void *ud);
size_t upb_arena_bytesallocated(const upb_arena *a);
void upb_arena_setnextblocksize(upb_arena *a, size_t size);
void upb_arena_setmaxblocksize(upb_arena *a, size_t size);
UPB_END_EXTERN_C
#ifdef __cplusplus
namespace upb {
class ErrorSpace;
class Status;
class upb::Arena {
public:
/* A simple arena with no initial memory block and the default allocator. */
Arena() { upb_arena_init(this); }
/* Constructs an arena with the given initial block which allocates blocks
* with the given allocator. The given allocator must outlive the Arena.
*
* If you pass NULL for the allocator it will default to the global allocator
* upb_alloc_global, and NULL/0 for the initial block will cause there to be
* no initial block. */
Arena(void *mem, size_t len, Allocator* a) {
upb_arena_init2(this, mem, len, a);
}
#endif
UPB_DECLARE_TYPE(upb::ErrorSpace, upb_errorspace)
UPB_DECLARE_TYPE(upb::Status, upb_status)
~Arena() { upb_arena_uninit(this); }
/* The maximum length of an error message before it will get truncated. */
#define UPB_STATUS_MAX_MESSAGE 128
/* Sets the size of the next block the Arena will request (unless the
* requested allocation is larger). Each block will double in size until the
* max limit is reached. */
void SetNextBlockSize(size_t size) { upb_arena_setnextblocksize(this, size); }
/* An error callback function is used to report errors from some component.
* The function can return "true" to indicate that the component should try
* to recover and proceed, but this is not always possible. */
typedef bool upb_errcb_t(void *closure, const upb_status* status);
/* Sets the maximum block size. No blocks larger than this will be requested
* from the underlying allocator unless individual arena allocations are
* larger. */
void SetMaxBlockSize(size_t size) { upb_arena_setmaxblocksize(this, size); }
/* Allows this arena to be used as a generic allocator.
*
* The arena does not need free() calls so when using Arena as an allocator
* it is safe to skip them. However they are no-ops so there is no harm in
* calling free() either. */
Allocator* allocator() { return upb_arena_alloc(this); }
/* Add a cleanup function to run when the arena is destroyed.
* Returns false on out-of-memory. */
bool AddCleanup(upb_cleanup_func* func, void* ud) {
return upb_arena_addcleanup(this, func, ud);
}
/* Total number of bytes that have been allocated. It is undefined what
* Realloc() does to this counter. */
size_t BytesAllocated() const {
return upb_arena_bytesallocated(this);
}
private:
UPB_DISALLOW_COPY_AND_ASSIGN(Arena)
#ifdef __cplusplus
class upb::ErrorSpace {
#else
struct upb_errorspace {
#endif
const char *name;
/* Should the error message in the status object according to this code. */
void (*set_message)(upb_status* status, int code);
struct upb_arena {
#endif /* __cplusplus */
/* We implement the allocator interface.
* This must be the first member of upb_arena! */
upb_alloc alloc;
/* Allocator to allocate arena blocks. We are responsible for freeing these
* when we are destroyed. */
upb_alloc *block_alloc;
size_t bytes_allocated;
size_t next_block_size;
size_t max_block_size;
/* Linked list of blocks. Points to an arena_block, defined in env.c */
void *block_head;
/* Cleanup entries. Pointer to a cleanup_ent, defined in env.c */
void *cleanup_head;
/* For future expansion, since the size of this struct is exposed to users. */
void *future1;
void *future2;
};
#ifdef __cplusplus
/* Object representing a success or failure status.
* It owns no resources and allocates no memory, so it should work
* even in OOM situations. */
/* upb::Environment ***********************************************************/
class upb::Status {
public:
Status();
/* A upb::Environment provides a means for injecting malloc and an
* error-reporting callback into encoders/decoders. This allows them to be
* independent of nearly all assumptions about their actual environment.
*
* It is also a container for allocating the encoders/decoders themselves that
* insulates clients from knowing their actual size. This provides ABI
* compatibility even if the size of the objects change. And this allows the
* structure definitions to be in the .c files instead of the .h files, making
* the .h files smaller and more readable.
*
* We might want to consider renaming this to "Pipeline" if/when the concept of
* a pipeline element becomes more formalized. */
UPB_DECLARE_TYPE(upb::Environment, upb_env)
/* Returns true if there is no error. */
bool ok() const;
/* A function that receives an error report from an encoder or decoder. The
* callback can return true to request that the error should be recovered, but
* if the error is not recoverable this has no effect. */
typedef bool upb_error_func(void *ud, const upb_status *status);
/* Optional error space and code, useful if the caller wants to
* programmatically check the specific kind of error. */
ErrorSpace* error_space();
int code() const;
UPB_BEGIN_EXTERN_C
const char *error_message() const;
void upb_env_init(upb_env *e);
void upb_env_uninit(upb_env *e);
/* The error message will be truncated if it is longer than
* UPB_STATUS_MAX_MESSAGE-4. */
void SetErrorMessage(const char* msg);
void SetFormattedErrorMessage(const char* fmt, ...);
void upb_env_initonly(upb_env *e);
/* If there is no error message already, this will use the ErrorSpace to
* populate the error message for this code. The caller can still call
* SetErrorMessage() to give a more specific message. */
void SetErrorCode(ErrorSpace* space, int code);
upb_arena *upb_env_arena(upb_env *e);
bool upb_env_ok(const upb_env *e);
void upb_env_seterrorfunc(upb_env *e, upb_error_func *func, void *ud);
/* Resets the status to a successful state with no message. */
void Clear();
/* Convenience wrappers around the methods of the contained arena. */
void upb_env_reporterrorsto(upb_env *e, upb_status *s);
bool upb_env_reporterror(upb_env *e, const upb_status *s);
void *upb_env_malloc(upb_env *e, size_t size);
void *upb_env_realloc(upb_env *e, void *ptr, size_t oldsize, size_t size);
void upb_env_free(upb_env *e, void *ptr);
bool upb_env_addcleanup(upb_env *e, upb_cleanup_func *func, void *ud);
size_t upb_env_bytesallocated(const upb_env *e);
UPB_END_EXTERN_C
#ifdef __cplusplus
class upb::Environment {
public:
/* The given Arena must outlive this environment. */
Environment() { upb_env_initonly(this); }
Environment(void *mem, size_t len, Allocator *a) : arena_(mem, len, a) {
upb_env_initonly(this);
}
void CopyFrom(const Status& other);
Arena* arena() { return upb_env_arena(this); }
/* Set a custom error reporting function. */
void SetErrorFunction(upb_error_func* func, void* ud) {
upb_env_seterrorfunc(this, func, ud);
}
/* Set the error reporting function to simply copy the status to the given
* status and abort. */
void ReportErrorsTo(Status* status) { upb_env_reporterrorsto(this, status); }
/* Returns true if all allocations and AddCleanup() calls have succeeded,
* and no errors were reported with ReportError() (except ones that recovered
* successfully). */
bool ok() const { return upb_env_ok(this); }
/* Reports an error to this environment's callback, returning true if
* the caller should try to recover. */
bool ReportError(const Status* status) {
return upb_env_reporterror(this, status);
}
private:
UPB_DISALLOW_COPY_AND_ASSIGN(Status)
UPB_DISALLOW_COPY_AND_ASSIGN(Environment)
#else
struct upb_status {
#endif
struct upb_env {
#endif /* __cplusplus */
upb_arena arena_;
upb_error_func *error_func_;
void *error_ud_;
bool ok_;
};
/* Specific status code defined by some error space (optional). */
int code_;
upb_errorspace *error_space_;
/* Error message; NULL-terminated. */
char msg[UPB_STATUS_MAX_MESSAGE];
};
/* upb::InlinedArena **********************************************************/
/* upb::InlinedEnvironment ****************************************************/
#define UPB_STATUS_INIT {true, 0, NULL, {0}}
/* upb::InlinedArena and upb::InlinedEnvironment seed their arenas with a
* predefined amount of memory. No heap memory will be allocated until the
* initial block is exceeded.
*
* These types only exist in C++ */
#ifdef __cplusplus
extern "C" {
#endif
/* The returned string is invalidated by any other call into the status. */
const char *upb_status_errmsg(const upb_status *status);
bool upb_ok(const upb_status *status);
upb_errorspace *upb_status_errspace(const upb_status *status);
int upb_status_errcode(const upb_status *status);
template <int N> class upb::InlinedArena : public upb::Arena {
public:
InlinedArena() : Arena(initial_block_, N, NULL) {}
explicit InlinedArena(Allocator* a) : Arena(initial_block_, N, a) {}
/* Any of the functions that write to a status object allow status to be NULL,
* to support use cases where the function's caller does not care about the
* status message. */
void upb_status_clear(upb_status *status);
void upb_status_seterrmsg(upb_status *status, const char *msg);
void upb_status_seterrf(upb_status *status, const char *fmt, ...);
void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args);
void upb_status_seterrcode(upb_status *status, upb_errorspace *space, int code);
void upb_status_copy(upb_status *to, const upb_status *from);
private:
UPB_DISALLOW_COPY_AND_ASSIGN(InlinedArena)
#ifdef __cplusplus
} /* extern "C" */
char initial_block_[N + UPB_ARENA_BLOCK_OVERHEAD];
};
namespace upb {
template <int N> class upb::InlinedEnvironment : public upb::Environment {
public:
InlinedEnvironment() : Environment(initial_block_, N, NULL) {}
explicit InlinedEnvironment(Allocator *a)
: Environment(initial_block_, N, a) {}
/* C++ Wrappers */
inline Status::Status() { Clear(); }
inline bool Status::ok() const { return upb_ok(this); }
inline const char* Status::error_message() const {
return upb_status_errmsg(this);
}
inline void Status::SetErrorMessage(const char* msg) {
upb_status_seterrmsg(this, msg);
}
inline void Status::SetFormattedErrorMessage(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
upb_status_vseterrf(this, fmt, args);
va_end(args);
}
inline void Status::SetErrorCode(ErrorSpace* space, int code) {
upb_status_seterrcode(this, space, code);
}
inline void Status::Clear() { upb_status_clear(this); }
inline void Status::CopyFrom(const Status& other) {
upb_status_copy(this, &other);
}
private:
UPB_DISALLOW_COPY_AND_ASSIGN(InlinedEnvironment)
char initial_block_[N + UPB_ARENA_BLOCK_OVERHEAD];
};
#endif /* __cplusplus */
} /* namespace upb */
#endif
#endif /* UPB_H_ */

Loading…
Cancel
Save