1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2016 Couchbase, Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #include <platform/cb_malloc.h>
19 
20 #include <cstring>
21 
22 // Which underlying memory allocator should we use?
23 #if defined(HAVE_JEMALLOC)
24 #  define MALLOC_PREFIX je_
25 /* Irrespective of how jemalloc was configured on this platform,
26  * don't rename je_FOO to FOO.
27  */
28 #  define JEMALLOC_NO_RENAME
29 #  include <jemalloc/jemalloc.h>
30 
31 #else /* system allocator */
32 #  define MALLOC_PREFIX
33 #if defined(HAVE_MALLOC_USABLE_SIZE)
34 #include <malloc.h>
35 #endif
36 #endif
37 
38 
39 // User-registered new and delete hooks
40 cb_malloc_new_hook_t cb_new_hook = nullptr;
41 cb_malloc_delete_hook_t cb_delete_hook = nullptr;
42 
43 // Macros to form the name of the memory allocation function to use based on
44 // the compile-time selected malloc library's prefix ('je_', 'tc_', '')
45 #define CONCAT(A, B) A ## B
46 #define CONCAT2(A, B) CONCAT(A, B)
47 #define MEM_ALLOC(name) CONCAT2(MALLOC_PREFIX, name)
48 
cb_malloc(size_t size)49 PLATFORM_PUBLIC_API void* cb_malloc(size_t size) throw() {
50     void* ptr = MEM_ALLOC(malloc)(size);
51     cb_invoke_new_hook(ptr, size);
52     return ptr;
53 }
54 
cb_calloc(size_t nmemb, size_t size)55 PLATFORM_PUBLIC_API void* cb_calloc(size_t nmemb, size_t size) throw() {
56     void* ptr = MEM_ALLOC(calloc)(nmemb, size);
57     cb_invoke_new_hook(ptr, nmemb * size);
58     return ptr;
59 }
60 
cb_realloc(void* ptr, size_t size)61 PLATFORM_PUBLIC_API void* cb_realloc(void* ptr, size_t size) throw() {
62     cb_invoke_delete_hook(ptr);
63     void* result = MEM_ALLOC(realloc)(ptr, size);
64     cb_invoke_new_hook(result, size);
65     return result;
66 }
67 
cb_free(void* ptr)68 PLATFORM_PUBLIC_API void cb_free(void* ptr) throw() {
69     cb_invoke_delete_hook(ptr);
70     return MEM_ALLOC(free)(ptr);
71 }
72 
cb_sized_free(void* ptr, size_t size)73 PLATFORM_PUBLIC_API void cb_sized_free(void* ptr, size_t size) throw() {
74     cb_invoke_delete_hook(ptr);
75 #if defined(HAVE_JEMALLOC_SDALLOCX)
76     if (ptr != nullptr) {
77         MEM_ALLOC(sdallocx)(ptr, size, /* no flags */ 0);
78     }
79 #else
80     MEM_ALLOC(free)(ptr);
81 #endif
82 }
83 
cb_strdup(const char* s1)84 PLATFORM_PUBLIC_API char* cb_strdup(const char* s1) {
85     size_t len = std::strlen(s1);
86     char* result = static_cast<char*>(cb_malloc(len + 1));
87     if (result != nullptr) {
88         std::strncpy(result, s1, len + 1);
89     }
90     return result;
91 }
92 
93 #if defined(HAVE_MALLOC_USABLE_SIZE)
cb_malloc_usable_size(void* ptr)94 PLATFORM_PUBLIC_API size_t cb_malloc_usable_size(void* ptr) throw() {
95     return MEM_ALLOC(malloc_usable_size)(ptr);
96 }
97 #endif
98 
99 /*
100  * Allocation / deallocation hook functions.
101  */
cb_add_new_hook(cb_malloc_new_hook_t f)102 bool cb_add_new_hook(cb_malloc_new_hook_t f) {
103     if (cb_new_hook == nullptr) {
104         cb_new_hook = f;
105         return true;
106     } else {
107         return false;
108     }
109 }
110 
cb_remove_new_hook(cb_malloc_new_hook_t f)111 bool cb_remove_new_hook(cb_malloc_new_hook_t f) {
112     if (cb_new_hook == f) {
113         cb_new_hook = nullptr;
114         return true;
115     } else {
116         return false;
117     }
118 }
119 
cb_add_delete_hook(cb_malloc_delete_hook_t f)120 bool cb_add_delete_hook(cb_malloc_delete_hook_t f) {
121     if (cb_delete_hook == nullptr) {
122         cb_delete_hook = f;
123         return true;
124     } else {
125         return false;
126     }
127 }
128 
cb_remove_delete_hook(cb_malloc_delete_hook_t f)129 bool cb_remove_delete_hook(cb_malloc_delete_hook_t f) {
130     if (cb_delete_hook == f) {
131         cb_delete_hook = nullptr;
132         return true;
133     } else {
134         return false;
135     }
136 }
137 
cb_invoke_new_hook(const void* ptr, size_t size)138 void cb_invoke_new_hook(const void* ptr, size_t size) {
139     if (cb_new_hook != nullptr) {
140         cb_new_hook(ptr, size);
141     }
142 }
143 
cb_invoke_delete_hook(const void* ptr)144 void cb_invoke_delete_hook(const void* ptr) {
145     if (cb_delete_hook != nullptr) {
146         cb_delete_hook(ptr);
147     }
148 }
149