1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-29 05:21:33 +03:00

Optimize usage of c_ptr(), c_ptr_quick() and String::alloc()

The problem was that when one used String::alloc() to allocate a string,
the String ensures that there is space for an extra NULL byte in the
buffer and if not, reallocates the string. This is a problem with the
String::set_int() that calls alloc(21), which forces extra
malloc/free calls to happen.

- We do not anymore re-allocate String if alloc() is called with the
  Allocated_length. This reduces number of malloc() allocations,
  especially one big re-allocation in Protocol::send_result_Set_metadata()
  for almost every query that produced a result to the connnected client.
- Avoid extra mallocs when using LONGLONG_BUFFER_SIZE
  This can now be done as alloc() doesn't increase buffers if new length is
  not bigger than old one.
- c_ptr() is redesigned to be safer (but a bit longer) than before.
- Remove wrong usage of c_ptr_quick()
  c_ptr_quick() was used in many cases to get the pointer to the used
  buffer, even when it didn't need to be \0 terminated. In this case
  ptr() is a better substitute.
  Another problem with c_ptr_quick() is that it did not guarantee that
  the string would be \0 terminated.
- item_val_str(), an API function not used currently by the server,
  now always returns a null terminated string (before it didn't always
  do that).
- Ensure that all String allocations uses STRING_PSI_MEMORY_KEY. The old
  mixed usage of performance keys caused assert's when String buffers
  where shrunk.
- Binary_string::shrink() is simplifed
- Fixed bug in String(const char *str, size_t len, CHARSET_INFO *cs) that
  used Binary_string((char *) str, len) instead of Binary_string(str,len).
- Changed argument to String() creations and String.set() functions to use
  'const char*' instead of 'char*'. This ensures that Alloced_length is
  not set, which gives safety against someone trying to change the
  original string. This also would allow us to use !Alloced_length in
  c_ptr() if needed.
- Changed string_ptr_cmp() to use memcmp() instead of c_ptr() to avoid
  a possible malloc during string comparision.
This commit is contained in:
Monty
2020-09-16 11:23:50 +03:00
committed by Sergei Golubchik
parent da85ad7987
commit 36cdd5c3cd
15 changed files with 123 additions and 71 deletions

View File

@ -600,25 +600,55 @@ public:
inline char *c_ptr()
{
DBUG_ASSERT(!alloced || !Ptr || !Alloced_length ||
(Alloced_length >= (str_length + 1)));
if (unlikely(!Ptr))
return (char*) "";
/*
Here we assume that any buffer used to initalize String has
an end \0 or have at least an accessable character at end.
This is to handle the case of String("Hello",5) and
String("hello",5) efficiently.
if (!Ptr || Ptr[str_length]) // Should be safe
(void) realloc(str_length);
We have two options here. To test for !Alloced_length or !alloced.
Using "Alloced_length" is slightly safer so that we do not read
from potentially unintialized memory (normally not dangerous but
may give warnings in valgrind), but "alloced" is safer as there
are less change to get memory loss from code that is using
String((char*), length) or String.set((char*), length) and does
not free things properly (and there is several places in the code
where this happens and it is hard to find out if any of these will call
c_ptr().
*/
if (unlikely(!alloced && !Ptr[str_length]))
return Ptr;
if (str_length < Alloced_length)
{
Ptr[str_length]=0;
return Ptr;
}
(void) realloc(str_length+1); /* This will add end \0 */
return Ptr;
}
/*
One should use c_ptr() instead for most cases. This will be deleted soon,
kept for compatiblity.
*/
inline char *c_ptr_quick()
{
if (Ptr && str_length < Alloced_length)
Ptr[str_length]=0;
return Ptr;
return c_ptr_safe();
}
/*
This is to be used only in the case when one cannot use c_ptr().
The cases are:
- When one initializes String with an external buffer and length and
buffer[length] could be uninitalized when c_ptr() is called.
- When valgrind gives warnings about uninitialized memory with c_ptr().
*/
inline char *c_ptr_safe()
{
if (Ptr && str_length < Alloced_length)
Ptr[str_length]=0;
else
(void) realloc(str_length);
(void) realloc(str_length + 1);
return Ptr;
}
@ -634,7 +664,16 @@ public:
}
inline bool alloc(size_t arg_length)
{
if (arg_length < Alloced_length)
/*
Allocate if we need more space or if we don't have p_done any
allocation yet (we don't want to have Ptr to be NULL for empty strings).
Note that if arg_length == Alloced_length then we don't allocate.
This ensures we don't do any extra allocations in protocol and String:int,
but the string will not be atomically null terminated if c_ptr() is not
called.
*/
if (arg_length <= Alloced_length && Alloced_length)
return 0;
return real_alloc(arg_length);
}
@ -642,7 +681,7 @@ public:
bool realloc_raw(size_t arg_length);
bool realloc(size_t arg_length)
{
if (realloc_raw(arg_length))
if (realloc_raw(arg_length+1))
return TRUE;
Ptr[arg_length]= 0; // This make other funcs shorter
return FALSE;
@ -743,7 +782,7 @@ public:
*/
String(const char *str, size_t len, CHARSET_INFO *cs)
:Charset(cs),
Binary_string((char *) str, len)
Binary_string(str, len)
{ }
String(char *str, size_t len, CHARSET_INFO *cs)
:Charset(cs),