Recordset dynamic storage.

JerryScript-DCO-1.0-Signed-off-by: Ruben Ayrapetyan r.ayrapetyan@samsung.com
This commit is contained in:
Ruben Ayrapetyan 2015-05-05 17:49:33 +03:00
parent fd0c943feb
commit 584149112d
3 changed files with 1300 additions and 0 deletions

View File

@ -0,0 +1,694 @@
/* Copyright 2015 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "rcs-chunked-list.h"
#include "rcs-recordset.h"
#include "jrt-bit-fields.h"
/** \addtogroup recordset Recordset
* @{
*
* Non-contiguous container abstraction with iterator.
*
* @{
*/
/**
* Get the record's type identifier
*
* @return record type identifier
*/
rcs_record_t::type_t
rcs_recordset_t::record_t::get_type (void)
const
{
JERRY_STATIC_ASSERT (sizeof (type_t) * JERRY_BITSINBYTE >= _type_field_width);
return (type_t) get_field (_type_field_pos, _type_field_width);
} /* rcs_recordset_t::record_t::get_type */
/**
* Set the record's type identifier
*/
void
rcs_recordset_t::record_t::set_type (rcs_record_t::type_t type) /**< record type identifier */
{
set_field (_type_field_pos, _type_field_width, type);
} /* rcs_recordset_t::record_t::set_type */
/**
* Compress pointer to extended compressed pointer
*
* @return dynamic storage-specific extended compressed pointer
*/
rcs_cpointer_t
rcs_recordset_t::record_t::cpointer_t::compress (rcs_record_t* pointer) /**< pointer to compress */
{
rcs_cpointer_t cpointer;
uintptr_t base_pointer = JERRY_ALIGNDOWN ((uintptr_t) pointer, MEM_ALIGNMENT);
uintptr_t diff = (uintptr_t) pointer - base_pointer;
JERRY_ASSERT (diff < MEM_ALIGNMENT);
JERRY_ASSERT (jrt_extract_bit_field (diff, 0, RCS_DYN_STORAGE_ALIGNMENT_LOG) == 0);
uintptr_t ext_part = (uintptr_t) jrt_extract_bit_field (diff,
RCS_DYN_STORAGE_ALIGNMENT_LOG,
MEM_ALIGNMENT_LOG - RCS_DYN_STORAGE_ALIGNMENT_LOG);
if ((void*) base_pointer == NULL)
{
cpointer.value.base_cp = MEM_CP_NULL;
}
else
{
cpointer.value.base_cp = mem_compress_pointer ((void*) base_pointer) & MEM_CP_MASK;
}
cpointer.value.ext = ext_part & ((1ull << (MEM_ALIGNMENT_LOG - RCS_DYN_STORAGE_ALIGNMENT_LOG)) - 1);
return cpointer;
} /* rcs_recordset_t::record_t::cpointer_t::compress */
/**
* Decompress extended compressed pointer
*
* @return decompressed pointer
*/
rcs_record_t*
rcs_recordset_t::record_t::cpointer_t::decompress (rcs_cpointer_t compressed_pointer) /**< recordset-specific
* compressed pointer */
{
uint8_t* base_pointer;
if (compressed_pointer.value.base_cp == MEM_CP_NULL)
{
base_pointer = NULL;
}
else
{
base_pointer = (uint8_t*) mem_decompress_pointer (compressed_pointer.value.base_cp);
}
uintptr_t diff = (uintptr_t) compressed_pointer.value.ext << RCS_DYN_STORAGE_ALIGNMENT_LOG;
return (rcs_recordset_t::record_t*) (base_pointer + diff);
} /* rcs_recordset_t::record_t::cpointer_t::decompress */
/**
* Assert that 'this' value points to correct record
*/
void
rcs_recordset_t::record_t::check_this (void)
const
{
JERRY_ASSERT (this != NULL);
uintptr_t ptr = (uintptr_t) this;
JERRY_ASSERT (JERRY_ALIGNUP (ptr, RCS_DYN_STORAGE_ALIGNMENT) == ptr);
} /* rcs_recordset_t::record_t::check_this */
/**
* Get value of the record's field with specified offset and width
*
* @return field's 32-bit unsigned integer value
*/
uint32_t
rcs_recordset_t::record_t::get_field (uint32_t field_pos, /**< offset, in bits */
uint32_t field_width) /**< width, in bits */
const
{
check_this ();
JERRY_ASSERT (sizeof (uint32_t) == RCS_DYN_STORAGE_LENGTH_UNIT);
JERRY_ASSERT (field_pos + field_width <= RCS_DYN_STORAGE_LENGTH_UNIT * JERRY_BITSINBYTE);
uint32_t value = *reinterpret_cast<const uint32_t*> (this);
return (uint32_t) jrt_extract_bit_field (value, field_pos, field_width);
} /* rcs_recordset_t::record_t::get_field */
/**
* Set value of the record's field with specified offset and width
*/
void
rcs_recordset_t::record_t::set_field (uint32_t field_pos, /**< offset, in bits */
uint32_t field_width, /**< width, in bits */
size_t value) /**< 32-bit unsigned integer value */
{
check_this ();
JERRY_ASSERT (sizeof (uint32_t) == RCS_DYN_STORAGE_LENGTH_UNIT);
JERRY_ASSERT (field_pos + field_width <= RCS_DYN_STORAGE_LENGTH_UNIT * JERRY_BITSINBYTE);
uint32_t prev_value = *reinterpret_cast<uint32_t*> (this);
*reinterpret_cast<uint32_t*> (this) = (uint32_t) jrt_set_bit_field_value (prev_value,
value,
field_pos,
field_width);
} /* rcs_recordset_t::record_t::set_field */
/**
* Get value of the record's pointer field with specified offset and width
*
* @return pointer to record
*/
rcs_record_t*
rcs_recordset_t::record_t::get_pointer (uint32_t field_pos, /**< offset, in bits */
uint32_t field_width) /**< width, in bits */
const
{
cpointer_t cpointer;
uint16_t value = (uint16_t) get_field (field_pos, field_width);
JERRY_ASSERT (sizeof (cpointer) == sizeof (cpointer.value));
JERRY_ASSERT (sizeof (value) == sizeof (cpointer.value));
cpointer.packed_value = value;
return cpointer_t::decompress (cpointer);
} /* rcs_recordset_t::record_t::get_pointer */
/**
* Set value of the record's pointer field with specified offset and width
*/
void
rcs_recordset_t::record_t::set_pointer (uint32_t field_pos, /**< offset, in bits */
uint32_t field_width, /**< width, in bits */
rcs_record_t* pointer_p) /**< pointer to a record */
{
cpointer_t cpointer = cpointer_t::compress (pointer_p);
set_field (field_pos, field_width, cpointer.packed_value);
} /* rcs_recordset_t::record_t::set_pointer */
/**
* Initialize record in specified place, and, if there is free space
* before next record, initialize free record for the space
*/
void
rcs_recordset_t::alloc_record_in_place (rcs_record_t* place_p, /**< where to initialize record */
size_t free_size, /**< size of free part between allocated record
* and next allocated record */
rcs_record_t* next_record_p) /**< next allocated record */
{
const size_t node_data_space_size = _chunk_list.get_data_space_size ();
if (next_record_p != NULL)
{
if (free_size == 0)
{
set_prev (next_record_p, place_p);
}
else
{
rcs_chunked_list_t::node_t* node_p = _chunk_list.get_node_from_pointer (next_record_p);
uint8_t* node_data_space_p = _chunk_list.get_data_space (node_p);
JERRY_ASSERT ((uint8_t*) next_record_p < node_data_space_p + node_data_space_size);
rcs_record_t* free_rec_p;
if ((uint8_t*) next_record_p >= node_data_space_p + free_size)
{
free_rec_p = (rcs_record_t*) ((uint8_t*) next_record_p - free_size);
}
else
{
size_t size_passed_back = (size_t) ((uint8_t*) next_record_p - node_data_space_p);
JERRY_ASSERT (size_passed_back < free_size && size_passed_back + node_data_space_size > free_size);
node_p = _chunk_list.get_prev (node_p);
node_data_space_p = _chunk_list.get_data_space (node_p);
free_rec_p = (rcs_record_t*) (node_data_space_p + node_data_space_size - \
(free_size - size_passed_back));
}
init_free_record (free_rec_p, free_size, place_p);
}
}
else if (free_size != 0)
{
rcs_chunked_list_t::node_t* node_p = _chunk_list.get_node_from_pointer (place_p);
JERRY_ASSERT (node_p != NULL);
rcs_chunked_list_t::node_t* next_node_p = _chunk_list.get_next (node_p);
while (next_node_p != NULL)
{
node_p = next_node_p;
next_node_p = _chunk_list.get_next (node_p);
}
uint8_t* node_data_space_p = _chunk_list.get_data_space (node_p);
const size_t node_data_space_size = _chunk_list.get_data_space_size ();
rcs_record_t* free_rec_p = (rcs_record_t*) (node_data_space_p + node_data_space_size \
- free_size);
init_free_record (free_rec_p, free_size, place_p);
}
} /* rcs_recordset_t::alloc_record_in_place */
/**
* Initialize specified record as free record
*/
void
rcs_recordset_t::init_free_record (rcs_record_t *rec_p, /**< record to init as free record */
size_t size, /**< size, including header */
rcs_record_t *prev_rec_p) /**< previous record (or NULL) */
{
rcs_free_record_t *free_rec_p = static_cast<rcs_free_record_t*> (rec_p);
free_rec_p->set_type (_free_record_type_id);
free_rec_p->set_size (size);
free_rec_p->set_prev (prev_rec_p);
} /* rcs_recordset_t::init_free_record */
/**
* Check if the record is free record
*/
bool
rcs_recordset_t::is_record_free (rcs_record_t *record_p) /**< a record */
{
JERRY_ASSERT (record_p != NULL);
return (record_p->get_type () == _free_record_type_id);
} /* rcs_recordset_t::is_record_free */
/**
* Allocate record of specified size
*
* @return record identifier
*/
rcs_record_t*
rcs_recordset_t::alloc_space_for_record (size_t bytes, /**< size */
rcs_record_t** out_prev_rec_p) /**< out: pointer to record, previous
* to the allocated, or NULL if the allocated
* record is the first */
{
assert_state_is_correct ();
JERRY_ASSERT (JERRY_ALIGNUP (bytes, RCS_DYN_STORAGE_ALIGNMENT) == bytes);
JERRY_ASSERT (out_prev_rec_p != NULL);
const size_t node_data_space_size = _chunk_list.get_data_space_size ();
*out_prev_rec_p = NULL;
for (rcs_record_t *rec_p = get_first ();
rec_p != NULL;
*out_prev_rec_p = rec_p, rec_p = get_next (rec_p))
{
if (is_record_free (rec_p))
{
size_t record_size = get_record_size (rec_p);
rcs_record_t* next_rec_p = get_next (rec_p);
if (record_size >= bytes)
{
/* record size is sufficient */
alloc_record_in_place (rec_p, record_size - bytes, next_rec_p);
return rec_p;
}
else
{
rcs_chunked_list_t::node_t* node_p = _chunk_list.get_node_from_pointer (rec_p);
uint8_t* node_data_space_p = _chunk_list.get_data_space (node_p);
uint8_t* node_data_space_end_p = node_data_space_p + node_data_space_size;
uint8_t* rec_space_p = (uint8_t*) rec_p;
if (rec_space_p + record_size >= node_data_space_end_p)
{
/* record lies up to end of node's data space size,
* and, so, can be extended up to necessary size */
while (record_size < bytes)
{
node_p = _chunk_list.insert_new (node_p);
record_size += node_data_space_size;
}
alloc_record_in_place (rec_p, record_size - bytes, next_rec_p);
return rec_p;
}
}
if (next_rec_p == NULL)
{
/* in the case, there are no more records in the storage,
* so, we should append new record */
break;
}
else
{
JERRY_ASSERT (!is_record_free (rec_p));
}
}
}
/* free record of sufficient size was not found */
rcs_chunked_list_t::node_t *node_p = _chunk_list.append_new ();
rcs_record_t* new_rec_p = (rcs_record_t*) _chunk_list.get_data_space (node_p);
size_t allocated_size = node_data_space_size;
while (allocated_size < bytes)
{
allocated_size += node_data_space_size;
_chunk_list.append_new ();
}
alloc_record_in_place (new_rec_p, allocated_size - bytes, NULL);
return new_rec_p;
} /* rcs_recordset_t::alloc_space_for_record */
/**
* Free specified record
*/
void
rcs_recordset_t::free_record (rcs_record_t* record_p) /**< record to free */
{
JERRY_ASSERT (record_p != NULL);
assert_state_is_correct ();
rcs_record_t *prev_rec_p = get_prev (record_p);
// make record free
init_free_record (record_p, get_record_size (record_p), prev_rec_p);
// merge adjacent free records, if there are any,
// and free nodes of chunked list that became unused
rcs_record_t *rec_from_p = record_p, *rec_to_p = get_next (record_p);
if (prev_rec_p != NULL
&& is_record_free (prev_rec_p))
{
rec_from_p = prev_rec_p;
prev_rec_p = get_prev (rec_from_p);
}
if (rec_to_p != NULL
&& is_record_free (rec_to_p))
{
rec_to_p = get_next (rec_to_p);
}
JERRY_ASSERT (rec_from_p != NULL && is_record_free (rec_from_p));
JERRY_ASSERT (rec_to_p == NULL || !is_record_free (rec_to_p));
rcs_chunked_list_t::node_t *node_from_p = _chunk_list.get_node_from_pointer (rec_from_p);
rcs_chunked_list_t::node_t *node_to_p;
if (rec_to_p == NULL)
{
node_to_p = NULL;
}
else
{
node_to_p = _chunk_list.get_node_from_pointer (rec_to_p);
}
const size_t node_data_space_size = _chunk_list.get_data_space_size ();
uint8_t* rec_from_beg_p = (uint8_t*) rec_from_p;
uint8_t* rec_to_beg_p = (uint8_t*) rec_to_p;
size_t free_size;
if (node_from_p == node_to_p)
{
JERRY_ASSERT (rec_from_beg_p + get_record_size (rec_from_p) <= rec_to_beg_p);
free_size = (size_t) (rec_to_beg_p - rec_from_beg_p);
}
else
{
for (rcs_chunked_list_t::node_t *iter_node_p = _chunk_list.get_next (node_from_p), *iter_next_node_p;
iter_node_p != node_to_p;
iter_node_p = iter_next_node_p)
{
iter_next_node_p = _chunk_list.get_next (iter_node_p);
_chunk_list.remove (iter_node_p);
}
JERRY_ASSERT (_chunk_list.get_next (node_from_p) == node_to_p);
size_t node_from_space = (size_t) (_chunk_list.get_data_space (node_from_p) +
node_data_space_size - rec_from_beg_p);
size_t node_to_space = (size_t) (node_to_p != NULL
? rec_to_beg_p - _chunk_list.get_data_space (node_to_p)
: 0);
free_size = node_from_space + node_to_space;
}
init_free_record (rec_from_p, free_size, prev_rec_p);
if (rec_to_p != NULL)
{
set_prev (rec_to_p, rec_from_p);
}
else if (prev_rec_p == NULL)
{
JERRY_ASSERT (node_to_p == NULL);
_chunk_list.remove (node_from_p);
JERRY_ASSERT (_chunk_list.get_first () == NULL);
}
assert_state_is_correct ();
} /* rcs_recordset_t::free_record */
/**
* Get first record
*
* @return pointer of the first record of the recordset
*/
rcs_record_t*
rcs_recordset_t::get_first (void)
{
rcs_chunked_list_t::node_t *first_node_p = _chunk_list.get_first ();
if (first_node_p == NULL)
{
return NULL;
}
else
{
return (rcs_record_t*) _chunk_list.get_data_space (first_node_p);
}
} /* rcs_recordset_t::get_first */
/**
* Get record, previous to the specified
*
* @return pointer to the previous record
*/
rcs_record_t*
rcs_recordset_t::get_prev (rcs_record_t* rec_p) /**< record */
{
JERRY_ASSERT (rec_p->get_type () == _free_record_type_id);
rcs_free_record_t *free_rec_p = static_cast<rcs_free_record_t*> (rec_p);
return free_rec_p->get_prev ();
} /* rcs_recordset_t::get_prev */
/**
* Get record, next to the specified
*
* @return pointer to the next record
*/
rcs_record_t*
rcs_recordset_t::get_next (rcs_record_t* rec_p) /**< record */
{
rcs_chunked_list_t::node_t* node_p = _chunk_list.get_node_from_pointer (rec_p);
const uint8_t* data_space_begin_p = _chunk_list.get_data_space (node_p);
const size_t data_space_size = _chunk_list.get_data_space_size ();
const uint8_t* record_start_p = (const uint8_t*) rec_p;
size_t record_size = get_record_size (rec_p);
size_t record_offset_in_node = (size_t) (record_start_p - data_space_begin_p);
size_t node_size_left = data_space_size - record_offset_in_node;
if (node_size_left > record_size)
{
return (rcs_record_t*) (record_start_p + record_size);
}
else
{
node_p = _chunk_list.get_next (node_p);
JERRY_ASSERT (node_p != NULL || record_size == node_size_left);
size_t record_size_left = record_size - node_size_left;
while (record_size_left >= data_space_size)
{
JERRY_ASSERT (node_p != NULL);
node_p = _chunk_list.get_next (node_p);
record_size_left -= data_space_size;
}
if (node_p == NULL)
{
JERRY_ASSERT (record_size_left == 0);
return NULL;
}
else
{
return (rcs_record_t*) (_chunk_list.get_data_space (node_p) + record_size_left);
}
}
} /* rcs_recordset_t::get_next */
/**
* Set previous record
*/
void
rcs_recordset_t::set_prev (rcs_record_t* rec_p, /**< record to set previous for */
rcs_record_t *prev_rec_p) /**< previous record */
{
JERRY_ASSERT (rec_p->get_type () == _free_record_type_id);
rcs_free_record_t *free_rec_p = static_cast<rcs_free_record_t*> (rec_p);
free_rec_p->set_prev (prev_rec_p);
} /* rcs_recordset_t::set_prev */
/**
* Get size of the record
*/
size_t
rcs_recordset_t::get_record_size (rcs_record_t* rec_p) /**< record */
{
JERRY_ASSERT (rec_p->get_type () == _free_record_type_id);
rcs_free_record_t *free_rec_p = static_cast<rcs_free_record_t*> (rec_p);
return free_rec_p->get_size ();
} /* rcs_recordset_t::get_record_size */
/**
* Assert that recordset state is correct
*/
void
rcs_recordset_t::assert_state_is_correct (void)
{
#ifndef JERRY_NDEBUG
size_t node_size_sum = 0;
size_t record_size_sum = 0;
rcs_record_t* last_record_p = NULL;
for (rcs_record_t* rec_p = get_first (), *next_rec_p;
rec_p != NULL;
last_record_p = rec_p, rec_p = next_rec_p)
{
record_size_sum += get_record_size (rec_p);
rcs_chunked_list_t::node_t *node_p = _chunk_list.get_node_from_pointer (rec_p);
next_rec_p = get_next (rec_p);
rcs_chunked_list_t::node_t *next_node_p;
if (next_rec_p == NULL)
{
next_node_p = NULL;
}
else
{
next_node_p = _chunk_list.get_node_from_pointer (next_rec_p);
}
while (node_p != next_node_p)
{
node_p = _chunk_list.get_next (node_p);
node_size_sum += _chunk_list.get_data_space_size ();
}
}
JERRY_ASSERT (node_size_sum == record_size_sum);
record_size_sum = 0;
for (rcs_record_t* rec_p = last_record_p;
rec_p != NULL;
rec_p = get_prev (rec_p))
{
record_size_sum += get_record_size (rec_p);
}
JERRY_ASSERT (node_size_sum == record_size_sum);
#endif /* !JERRY_NDEBUG */
} /* rcs_recordset_t::assert_state_is_correct */
/**
* Get size of the free record
*/
size_t
rcs_free_record_t::get_size (void)
const
{
return get_field (_length_field_pos, _length_field_width) * RCS_DYN_STORAGE_LENGTH_UNIT;
} /* rcs_free_record_t::get_size */
/**
* Set size of the free record
*/
void
rcs_free_record_t::set_size (size_t size) /**< size to set */
{
JERRY_ASSERT (JERRY_ALIGNUP (size, RCS_DYN_STORAGE_ALIGNMENT) == size);
set_field (_length_field_pos, _length_field_width, size >> RCS_DYN_STORAGE_ALIGNMENT_LOG);
} /* rcs_free_record_t::set_size */
/**
* Get previous record for the free record
*/
rcs_record_t*
rcs_free_record_t::get_prev (void)
const
{
return get_pointer (_prev_field_pos, _prev_field_width);
} /* rcs_free_record_t::get_prev */
/**
* Set previous record for the free record
*/
void
rcs_free_record_t::set_prev (rcs_record_t* prev_rec_p) /**< previous record to set */
{
set_pointer (_prev_field_pos, _prev_field_width, prev_rec_p);
} /* rcs_free_record_t::set_prev */
/**
* @}
*/

View File

@ -0,0 +1,274 @@
/* Copyright 2015 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef RCS_RECORDSET_H
#define RCS_RECORDSET_H
#include "jrt.h"
#include "jrt-bit-fields.h"
#include "mem-allocator.h"
#include "rcs-chunked-list.h"
/** \addtogroup recordset Recordset
* @{
*
* Non-contiguous container abstraction with iterator.
*
* @{
*/
/**
* Logarithm of a dynamic storage unit alignment
*/
#define RCS_DYN_STORAGE_ALIGNMENT_LOG (2u)
/**
* Dynamic storage unit alignment
*/
#define RCS_DYN_STORAGE_ALIGNMENT (1ull << RCS_DYN_STORAGE_ALIGNMENT_LOG)
/**
* Unit of length
*
* See also:
* rcs_dyn_storage_length_t
*/
#define RCS_DYN_STORAGE_LENGTH_UNIT (4u)
/**
* Dynamic storage
*
* Note:
* Static C++ constructors / desctructors are supposed to be not supported.
* So, initialization / destruction is implemented through init / finalize
* static functions.
*/
class rcs_recordset_t
{
public:
/* Constructor */
void init (void)
{
_chunk_list.init ();
JERRY_ASSERT (_chunk_list.get_data_space_size () % RCS_DYN_STORAGE_LENGTH_UNIT == 0);
} /* init */
/* Destructor */
void finalize (void)
{
_chunk_list.free ();
} /* finalize */
/**
* Record type
*/
class record_t
{
public:
typedef uint8_t type_t;
type_t get_type (void) const;
void set_type (type_t type);
/**
* Dynamic storage-specific extended compressed pointer
*
* Note:
* the pointer can represent addresses aligned by RCS_DYN_STORAGE_ALIGNMENT,
* while mem_cpointer_t can only represent addressed aligned by MEM_ALIGNMENT.
*/
struct cpointer_t
{
static const uint32_t bit_field_width = MEM_CP_WIDTH + MEM_ALIGNMENT_LOG - RCS_DYN_STORAGE_ALIGNMENT_LOG;
union
{
struct
{
mem_cpointer_t base_cp : MEM_CP_WIDTH; /**< pointer to base of addressed area */
#if MEM_ALIGNMENT_LOG > RCS_DYN_STORAGE_ALIGNMENT_LOG
uint16_t ext : (MEM_ALIGNMENT_LOG - RCS_DYN_STORAGE_ALIGNMENT_LOG); /**< extension of the basic
* compressed pointer
* used for more detailed
* addressing */
#endif /* MEM_ALIGNMENT_LOG > RCS_DYN_STORAGE_ALIGNMENT_LOG */
} value;
uint16_t packed_value;
};
static cpointer_t compress (record_t* pointer_p);
static record_t* decompress (cpointer_t pointer_cp);
};
private:
/**
* Offset of 'type' field, in bits
*/
static const uint32_t _type_field_pos = 0u;
/**
* Width of 'type' field, in bits
*/
static const uint32_t _type_field_width = 4u;
protected:
void check_this (void) const;
uint32_t get_field (uint32_t field_pos, uint32_t field_width) const;
void set_field (uint32_t field_pos, uint32_t field_width, size_t value);
record_t* get_pointer (uint32_t field_pos, uint32_t field_width) const;
void set_pointer (uint32_t field_pos, uint32_t field_width, record_t* pointer_p);
/**
* Offset of a derived record's fields, in bits
*/
static const uint32_t _fields_offset_begin = _type_field_pos + _type_field_width;
};
private:
friend class rcs_record_iterator_t;
/**
* Type identifier for free record
*/
static const record_t::type_t _free_record_type_id = 0;
/**
* Chunked list used for memory allocation
*/
rcs_chunked_list_t _chunk_list;
void alloc_record_in_place (record_t* place_p,
size_t free_size,
record_t* next_record_p);
void init_free_record (record_t *free_rec_p, size_t size, record_t *prev_rec_p);
bool is_record_free (record_t *record_p);
protected:
/**
* First type identifier that can be used for storage-specific record types
*/
static const record_t::type_t _first_type_id = _free_record_type_id + 1;
/**
* Allocate new record of specified type
*
* @return pointer to the new record
*/
template<
typename T, /**< type of record structure */
typename ... SizeArgs> /**< type of arguments of T::size */
T* alloc_record (record_t::type_t type, /**< record's type identifier */
SizeArgs ... size_args) /**< arguments of T::size */
{
JERRY_ASSERT (type >= _first_type_id);
size_t size = T::size (size_args...);
record_t *prev_rec_p;
T* rec_p = static_cast<T*> (alloc_space_for_record (size, &prev_rec_p));
rec_p->set_type (type);
rec_p->set_size (size);
rec_p->set_prev (prev_rec_p);
assert_state_is_correct ();
return rec_p;
} /* alloc_record */
record_t* alloc_space_for_record (size_t bytes, record_t** out_prev_rec_p);
void free_record (record_t* record_p);
record_t* get_first (void);
virtual record_t* get_prev (record_t* rec_p);
record_t* get_next (record_t* rec_p);
virtual void set_prev (record_t* rec_p, record_t *prev_rec_p);
virtual size_t get_record_size (record_t* rec_p);
void assert_state_is_correct (void);
}; /* rcs_recordset_t */
/**
* Record type
*/
typedef rcs_recordset_t::record_t rcs_record_t;
/**
* Recordset-specific compressed pointer type
*/
typedef rcs_record_t::cpointer_t rcs_cpointer_t;
/**
* Record iterator
*/
class rcs_record_iterator_t
{
public:
rcs_record_iterator_t (rcs_record_t* rec_p);
rcs_record_iterator_t (rcs_cpointer_t rec_ext_cp);
protected:
template<typename T> T read (void);
template<typename T> void write (T value);
private:
rcs_record_t* _record_start_p; /**< start of current record */
uint8_t* _current_pos_p; /**< pointer to current offset in current record */
rcs_recordset_t *_recordset_p; /**< recordset containing the record */
}; /* rcs_record_iterator_t */
/**
* Free record layout description
*/
class rcs_free_record_t : public rcs_record_t
{
public:
size_t get_size (void) const;
void set_size (size_t size);
rcs_record_t* get_prev (void) const;
void set_prev (rcs_record_t* prev_rec_p);
private:
/**
* Offset of 'length' field, in bits
*/
static const uint32_t _length_field_pos = _fields_offset_begin;
/**
* Width of 'length' field, in bits
*/
static const uint32_t _length_field_width = 12u;
/**
* Offset of 'previous record' field, in bits
*/
static const uint32_t _prev_field_pos = _length_field_pos + _length_field_width;
/**
* Width of 'previous record' field, in bits
*/
static const uint32_t _prev_field_width = rcs_cpointer_t::bit_field_width;
};
/**
* @}
*/
#endif /* RCS_RECORDSET_H */

View File

@ -0,0 +1,332 @@
/* Copyright 2015 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include "jrt.h"
#include "mem-allocator.h"
#include "rcs-recordset.h"
extern "C"
{
extern void srand (unsigned int __seed);
extern int rand (void);
extern long int time (long int *__timer);
extern int printf (__const char *__restrict __format, ...);
extern void *memset (void *__s, int __c, size_t __n);
}
// Heap size is 32K
#define test_heap_size (32 * 1024)
// Iterations count
#define test_iters (64)
// Subiterations count
#define test_sub_iters 64
// Threshold size of block to allocate
#define test_threshold_block_size 8192
// Maximum number of elements in a type-one record
#define test_max_type_one_record_elements 64
class test_rcs_record_type_one_t : public rcs_record_t
{
public:
static size_t size (uint32_t elements_count)
{
return JERRY_ALIGNUP (header_size + element_size * elements_count,
RCS_DYN_STORAGE_LENGTH_UNIT);
}
size_t get_size () const
{
return get_field (length_field_pos, length_field_width) * RCS_DYN_STORAGE_LENGTH_UNIT;
}
void set_size (size_t size)
{
JERRY_ASSERT (JERRY_ALIGNUP (size, RCS_DYN_STORAGE_ALIGNMENT) == size);
set_field (length_field_pos, length_field_width, size >> RCS_DYN_STORAGE_ALIGNMENT_LOG);
}
rcs_record_t* get_prev () const
{
return get_pointer (prev_field_pos, prev_field_width);
}
void set_prev (rcs_record_t* prev_rec_p)
{
set_pointer (prev_field_pos, prev_field_width, prev_rec_p);
}
private:
static const uint32_t length_field_pos = _fields_offset_begin;
static const uint32_t length_field_width = 12u;
static const uint32_t prev_field_pos = length_field_pos + length_field_width;
static const uint32_t prev_field_width = rcs_cpointer_t::bit_field_width;
static const size_t header_size = 2 * RCS_DYN_STORAGE_LENGTH_UNIT;
static const size_t element_size = sizeof (uint16_t);
};
class test_rcs_record_type_two_t : public rcs_record_t
{
public:
static size_t size (void)
{
return JERRY_ALIGNUP (header_size, RCS_DYN_STORAGE_LENGTH_UNIT);
}
size_t get_size () const
{
return size ();
}
void set_size (size_t size)
{
JERRY_ASSERT (size == get_size ());
}
rcs_record_t* get_prev () const
{
return get_pointer (prev_field_pos, prev_field_width);
}
void set_prev (rcs_record_t* prev_rec_p)
{
set_pointer (prev_field_pos, prev_field_width, prev_rec_p);
}
private:
static const uint32_t prev_field_pos = _fields_offset_begin;
static const uint32_t prev_field_width = rcs_cpointer_t::bit_field_width;
static const size_t header_size = RCS_DYN_STORAGE_LENGTH_UNIT;
};
class test_rcs_recordset_t : public rcs_recordset_t
{
public:
test_rcs_record_type_one_t*
create_record_type_one (uint32_t elements_count)
{
return alloc_record<test_rcs_record_type_one_t, uint32_t> (_record_type_one_id,
elements_count);
}
void
free_record_type_one (test_rcs_record_type_one_t* rec_p)
{
free_record (rec_p);
}
test_rcs_record_type_two_t*
create_record_type_two (void)
{
return alloc_record<test_rcs_record_type_two_t> (_record_type_two_id);
}
void
free_record_type_two (test_rcs_record_type_two_t* rec_p)
{
free_record (rec_p);
}
private:
static const int _record_type_one_id = _first_type_id + 0;
static const int _record_type_two_id = _first_type_id + 1;
virtual rcs_record_t* get_prev (rcs_record_t* rec_p)
{
switch (rec_p->get_type ())
{
case _record_type_one_id:
{
return (static_cast<test_rcs_record_type_one_t*> (rec_p))->get_prev ();
}
case _record_type_two_id:
{
return (static_cast<test_rcs_record_type_two_t*> (rec_p))->get_prev ();
}
default:
{
JERRY_ASSERT (rec_p->get_type () < _first_type_id);
return rcs_recordset_t::get_prev (rec_p);
}
}
}
virtual void set_prev (rcs_record_t* rec_p,
rcs_record_t *prev_rec_p)
{
switch (rec_p->get_type ())
{
case _record_type_one_id:
{
return (static_cast<test_rcs_record_type_one_t*> (rec_p))->set_prev (prev_rec_p);
}
case _record_type_two_id:
{
return (static_cast<test_rcs_record_type_two_t*> (rec_p))->set_prev (prev_rec_p);
}
default:
{
JERRY_ASSERT (rec_p->get_type () < _first_type_id);
return rcs_recordset_t::set_prev (rec_p, prev_rec_p);
}
}
}
virtual size_t get_record_size (rcs_record_t* rec_p)
{
switch (rec_p->get_type ())
{
case _record_type_one_id:
{
return (static_cast<test_rcs_record_type_one_t*> (rec_p))->get_size ();
}
case _record_type_two_id:
{
return (static_cast<test_rcs_record_type_two_t*> (rec_p))->get_size ();
}
default:
{
JERRY_ASSERT (rec_p->get_type () < _first_type_id);
return rcs_recordset_t::get_record_size (rec_p);
}
}
}
};
int
main (int __attr_unused___ argc,
char __attr_unused___ **argv)
{
srand ((unsigned int) time (NULL));
int k = rand ();
printf ("seed=%d\n", k);
srand ((unsigned int) k);
mem_init ();
test_rcs_recordset_t storage;
storage.init ();
for (uint32_t i = 0; i < test_iters; i++)
{
test_rcs_record_type_one_t *type_one_records[test_sub_iters];
uint32_t type_one_record_element_counts[test_sub_iters];
int type_one_records_number = 0;
test_rcs_record_type_two_t *type_two_records[test_sub_iters];
int type_two_records_number = 0;
for (uint32_t j = 0; j < test_sub_iters; j++)
{
if (rand () % 2)
{
assert (type_one_records_number < test_sub_iters);
uint32_t elements_count = ((uint32_t) rand ()) % test_max_type_one_record_elements;
type_one_record_element_counts[type_one_records_number] = elements_count;
type_one_records[type_one_records_number] = storage.create_record_type_one (elements_count);
assert (type_one_records[type_one_records_number] != NULL);
type_one_records_number++;
}
else
{
assert (type_two_records_number < test_sub_iters);
type_two_records[type_two_records_number] = storage.create_record_type_two ();
assert (type_two_records[type_two_records_number] != NULL);
type_two_records_number++;
}
}
while (type_one_records_number + type_two_records_number != 0)
{
bool free_type_one;
if (type_one_records_number == 0)
{
assert (type_two_records_number != 0);
free_type_one = false;
}
else if (type_two_records_number == 0)
{
assert (type_one_records_number != 0);
free_type_one = true;
}
else
{
free_type_one = ((rand () % 2) != 0);
}
if (free_type_one)
{
assert (type_one_records_number > 0);
int index_to_free = (rand () % type_one_records_number);
assert (index_to_free >= 0 && index_to_free < type_one_records_number);
storage.free_record_type_one (type_one_records[index_to_free]);
type_one_records_number--;
while (index_to_free < type_one_records_number)
{
type_one_records[index_to_free] = type_one_records[index_to_free + 1];
type_one_record_element_counts[index_to_free] = type_one_record_element_counts[index_to_free + 1];
index_to_free++;
}
}
else
{
assert (type_two_records_number > 0);
int index_to_free = (rand () % type_two_records_number);
assert (index_to_free >= 0 && index_to_free < type_two_records_number);
storage.free_record_type_two (type_two_records[index_to_free]);
type_two_records_number--;
while (index_to_free < type_two_records_number)
{
type_two_records[index_to_free] = type_two_records[index_to_free + 1];
index_to_free++;
}
}
}
}
storage.finalize ();
mem_finalize (true);
return 0;
} /* main */