/* * upb - a minimalist implementation of protocol buffers. * * Copyright (c) 2014 Google Inc. See LICENSE for details. * Author: Josh Haberman * * 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); // An environment is *not* thread-safe. UPB_DEFINE_CLASS0(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 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); , UPB_DEFINE_STRUCT0(upb_env, 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 // An allocator that allocates from an initial memory region (likely the stack) // before falling back to another allocator. UPB_DEFINE_CLASS0(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); , UPB_DEFINE_STRUCT0(upb_seededalloc, // 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_