pidgin/pidgin
Clone
Summary
Browse
Changes
Graph
Fix some issues found in review
2019-09-29, Gary Kramlich
0b17c29da855
Fix some issues found in review
/*
* Purple
*
* Purple is the legal property of its developers, whose names are too
* numerous to list here. Please refer to the COPYRIGHT file distributed
* with this source distribution
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#include
"memorypool.h"
#include
<string.h>
#define PURPLE_MEMORY_POOL_BLOCK_PADDING (sizeof(guint64))
#define PURPLE_MEMORY_POINTER_SHIFT(pointer, value) \
(gpointer)((guintptr)(pointer) + (value))
#define PURPLE_MEMORY_PADDED(pointer, padding) \
(gpointer)((((guintptr)(pointer) - 1) / (padding) + 1) * padding)
#define PURPLE_MEMORY_POOL_DEFAULT_BLOCK_SIZE 1024
#define PURPLE_MEMORY_POOL_DISABLED FALSE
typedef
struct
_PurpleMemoryPoolBlock
PurpleMemoryPoolBlock
;
typedef
struct
{
gboolean
disabled
;
gulong
block_size
;
PurpleMemoryPoolBlock
*
first_block
;
PurpleMemoryPoolBlock
*
last_block
;
}
PurpleMemoryPoolPrivate
;
struct
_PurpleMemoryPoolBlock
{
gpointer
available_ptr
;
gpointer
end_ptr
;
PurpleMemoryPoolBlock
*
next
;
};
enum
{
PROP_ZERO
,
PROP_BLOCK_SIZE
,
PROP_LAST
};
static
GParamSpec
*
properties
[
PROP_LAST
];
G_DEFINE_TYPE_WITH_PRIVATE
(
PurpleMemoryPool
,
purple_memory_pool
,
G_TYPE_OBJECT
);
/*******************************************************************************
* Memory allocation/deallocation
******************************************************************************/
static
PurpleMemoryPoolBlock
*
purple_memory_pool_block_new
(
gulong
block_size
)
{
gpointer
block_raw
;
PurpleMemoryPoolBlock
*
block
;
gsize
total_size
;
/* ceil block struct size to the multipy of align */
total_size
=
((
sizeof
(
PurpleMemoryPoolBlock
)
-
1
)
/
PURPLE_MEMORY_POOL_BLOCK_PADDING
+
1
)
*
sizeof
(
PurpleMemoryPoolBlock
);
g_return_val_if_fail
(
block_size
<
G_MAXSIZE
-
total_size
,
NULL
);
total_size
+=
block_size
;
block_raw
=
g_try_malloc
(
total_size
);
g_return_val_if_fail
(
block_raw
!=
NULL
,
NULL
);
block
=
block_raw
;
/* in fact, we don't set available_ptr padded to
* PURPLE_MEMORY_POOL_BLOCK_PADDING, but we guarantee, there is at least
* block_size long block if padded to that value. */
block
->
available_ptr
=
PURPLE_MEMORY_POINTER_SHIFT
(
block_raw
,
sizeof
(
PurpleMemoryPoolBlock
));
block
->
end_ptr
=
PURPLE_MEMORY_POINTER_SHIFT
(
block_raw
,
total_size
);
block
->
next
=
NULL
;
return
block
;
}
static
gpointer
purple_memory_pool_alloc_impl
(
PurpleMemoryPool
*
pool
,
gsize
size
,
guint
alignment
)
{
PurpleMemoryPoolPrivate
*
priv
=
NULL
;
PurpleMemoryPoolBlock
*
blk
;
gpointer
mem
=
NULL
;
g_return_val_if_fail
(
PURPLE_IS_MEMORY_POOL
(
pool
),
NULL
);
priv
=
purple_memory_pool_get_instance_private
(
pool
);
if
(
priv
->
disabled
)
{
/* XXX: this may cause some leaks */
return
g_try_malloc
(
size
);
}
g_return_val_if_fail
(
alignment
<=
PURPLE_MEMORY_POOL_BLOCK_PADDING
,
NULL
);
g_warn_if_fail
(
alignment
>=
1
);
if
(
alignment
<
1
)
alignment
=
1
;
blk
=
priv
->
last_block
;
if
(
blk
)
{
mem
=
PURPLE_MEMORY_PADDED
(
blk
->
available_ptr
,
alignment
);
if
(
mem
>=
blk
->
end_ptr
)
mem
=
NULL
;
else
if
(
mem
<
blk
->
available_ptr
)
/* gpointer overflow */
mem
=
NULL
;
else
if
(
PURPLE_MEMORY_POINTER_SHIFT
(
mem
,
size
)
>=
blk
->
end_ptr
)
mem
=
NULL
;
}
if
(
mem
==
NULL
)
{
gsize
real_size
=
priv
->
block_size
;
if
(
real_size
<
size
)
real_size
=
size
;
blk
=
purple_memory_pool_block_new
(
real_size
);
g_return_val_if_fail
(
blk
!=
NULL
,
NULL
);
g_assert
((
priv
->
first_block
==
NULL
)
==
(
priv
->
last_block
==
NULL
));
if
(
priv
->
first_block
==
NULL
)
{
priv
->
first_block
=
blk
;
priv
->
last_block
=
blk
;
}
else
{
priv
->
last_block
->
next
=
blk
;
priv
->
last_block
=
blk
;
}
mem
=
PURPLE_MEMORY_PADDED
(
blk
->
available_ptr
,
alignment
);
g_assert
((
guintptr
)
mem
+
size
<
(
guintptr
)
blk
->
end_ptr
);
g_assert
(
mem
>=
blk
->
available_ptr
);
/* gpointer overflow */
}
g_assert
(
blk
!=
NULL
);
g_assert
(
mem
!=
NULL
);
blk
->
available_ptr
=
PURPLE_MEMORY_POINTER_SHIFT
(
mem
,
size
);
g_assert
(
blk
->
available_ptr
<=
blk
->
end_ptr
);
return
mem
;
}
static
void
purple_memory_pool_cleanup_impl
(
PurpleMemoryPool
*
pool
)
{
PurpleMemoryPoolPrivate
*
priv
=
purple_memory_pool_get_instance_private
(
pool
);
PurpleMemoryPoolBlock
*
blk
;
blk
=
priv
->
first_block
;
priv
->
first_block
=
NULL
;
priv
->
last_block
=
NULL
;
while
(
blk
)
{
PurpleMemoryPoolBlock
*
next
=
blk
->
next
;
g_free
(
blk
);
blk
=
next
;
}
}
/*******************************************************************************
* API implementation
******************************************************************************/
void
purple_memory_pool_set_block_size
(
PurpleMemoryPool
*
pool
,
gulong
block_size
)
{
PurpleMemoryPoolPrivate
*
priv
=
NULL
;
g_return_if_fail
(
PURPLE_IS_MEMORY_POOL
(
pool
));
priv
=
purple_memory_pool_get_instance_private
(
pool
);
priv
->
block_size
=
block_size
;
g_object_notify_by_pspec
(
G_OBJECT
(
pool
),
properties
[
PROP_BLOCK_SIZE
]);
}
gpointer
purple_memory_pool_alloc
(
PurpleMemoryPool
*
pool
,
gsize
size
,
guint
alignment
)
{
PurpleMemoryPoolClass
*
klass
;
if
(
size
==
0
)
return
NULL
;
g_return_val_if_fail
(
PURPLE_IS_MEMORY_POOL
(
pool
),
NULL
);
klass
=
PURPLE_MEMORY_POOL_GET_CLASS
(
pool
);
g_return_val_if_fail
(
klass
!=
NULL
,
NULL
);
g_return_val_if_fail
(
klass
->
palloc
!=
NULL
,
NULL
);
return
klass
->
palloc
(
pool
,
size
,
alignment
);
}
gpointer
purple_memory_pool_alloc0
(
PurpleMemoryPool
*
pool
,
gsize
size
,
guint
alignment
)
{
gpointer
mem
;
if
(
size
==
0
)
return
NULL
;
mem
=
purple_memory_pool_alloc
(
pool
,
size
,
alignment
);
g_return_val_if_fail
(
mem
!=
NULL
,
NULL
);
memset
(
mem
,
0
,
size
);
return
mem
;
}
void
purple_memory_pool_free
(
PurpleMemoryPool
*
pool
,
gpointer
mem
)
{
PurpleMemoryPoolClass
*
klass
;
if
(
mem
==
NULL
)
return
;
g_return_if_fail
(
PURPLE_IS_MEMORY_POOL
(
pool
));
klass
=
PURPLE_MEMORY_POOL_GET_CLASS
(
pool
);
g_return_if_fail
(
klass
!=
NULL
);
if
(
klass
->
pfree
)
klass
->
pfree
(
pool
,
mem
);
}
void
purple_memory_pool_cleanup
(
PurpleMemoryPool
*
pool
)
{
PurpleMemoryPoolClass
*
klass
;
g_return_if_fail
(
PURPLE_IS_MEMORY_POOL
(
pool
));
klass
=
PURPLE_MEMORY_POOL_GET_CLASS
(
pool
);
g_return_if_fail
(
klass
!=
NULL
);
klass
->
cleanup
(
pool
);
}
/*******************************************************************************
* Object stuff
******************************************************************************/
PurpleMemoryPool
*
purple_memory_pool_new
(
void
)
{
return
g_object_new
(
PURPLE_TYPE_MEMORY_POOL
,
NULL
);
}
static
void
purple_memory_pool_init
(
PurpleMemoryPool
*
pool
)
{
PurpleMemoryPoolPrivate
*
priv
=
purple_memory_pool_get_instance_private
(
pool
);
priv
->
disabled
=
PURPLE_MEMORY_POOL_DISABLED
;
}
static
void
purple_memory_pool_finalize
(
GObject
*
obj
)
{
purple_memory_pool_cleanup
(
PURPLE_MEMORY_POOL
(
obj
));
G_OBJECT_CLASS
(
purple_memory_pool_parent_class
)
->
finalize
(
obj
);
}
static
void
purple_memory_pool_get_property
(
GObject
*
obj
,
guint
param_id
,
GValue
*
value
,
GParamSpec
*
pspec
)
{
PurpleMemoryPool
*
pool
=
PURPLE_MEMORY_POOL
(
obj
);
PurpleMemoryPoolPrivate
*
priv
=
purple_memory_pool_get_instance_private
(
pool
);
switch
(
param_id
)
{
case
PROP_BLOCK_SIZE
:
g_value_set_ulong
(
value
,
priv
->
block_size
);
break
;
default
:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
obj
,
param_id
,
pspec
);
}
}
static
void
purple_memory_pool_set_property
(
GObject
*
obj
,
guint
param_id
,
const
GValue
*
value
,
GParamSpec
*
pspec
)
{
PurpleMemoryPool
*
pool
=
PURPLE_MEMORY_POOL
(
obj
);
PurpleMemoryPoolPrivate
*
priv
=
purple_memory_pool_get_instance_private
(
pool
);
switch
(
param_id
)
{
case
PROP_BLOCK_SIZE
:
priv
->
block_size
=
g_value_get_ulong
(
value
);
break
;
default
:
G_OBJECT_WARN_INVALID_PROPERTY_ID
(
obj
,
param_id
,
pspec
);
}
}
static
void
purple_memory_pool_class_init
(
PurpleMemoryPoolClass
*
klass
)
{
GObjectClass
*
obj_class
=
G_OBJECT_CLASS
(
klass
);
obj_class
->
finalize
=
purple_memory_pool_finalize
;
obj_class
->
get_property
=
purple_memory_pool_get_property
;
obj_class
->
set_property
=
purple_memory_pool_set_property
;
klass
->
palloc
=
purple_memory_pool_alloc_impl
;
klass
->
cleanup
=
purple_memory_pool_cleanup_impl
;
properties
[
PROP_BLOCK_SIZE
]
=
g_param_spec_ulong
(
"block-size"
,
"Block size"
,
"The default size of each block of pool memory."
,
0
,
G_MAXULONG
,
PURPLE_MEMORY_POOL_DEFAULT_BLOCK_SIZE
,
G_PARAM_READWRITE
|
G_PARAM_CONSTRUCT
|
G_PARAM_STATIC_STRINGS
);
g_object_class_install_properties
(
obj_class
,
PROP_LAST
,
properties
);
}
gchar
*
purple_memory_pool_strdup
(
PurpleMemoryPool
*
pool
,
const
gchar
*
str
)
{
gsize
str_len
;
gchar
*
str_dup
;
if
(
str
==
NULL
)
return
NULL
;
g_return_val_if_fail
(
PURPLE_IS_MEMORY_POOL
(
pool
),
NULL
);
str_len
=
strlen
(
str
);
str_dup
=
purple_memory_pool_alloc
(
pool
,
str_len
+
1
,
sizeof
(
gchar
));
g_return_val_if_fail
(
str_dup
!=
NULL
,
NULL
);
memcpy
(
str_dup
,
str
,
str_len
);
str_dup
[
str_len
]
=
'\0'
;
return
str_dup
;
}