diff --git a/jerry-core/rcs/rcs-recordset.cpp b/jerry-core/rcs/rcs-recordset.cpp new file mode 100644 index 0000000000..2b9fbae608 --- /dev/null +++ b/jerry-core/rcs/rcs-recordset.cpp @@ -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 (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 (this); + *reinterpret_cast (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 (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 (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 (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 (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 */ + +/** + * @} + */ diff --git a/jerry-core/rcs/rcs-recordset.h b/jerry-core/rcs/rcs-recordset.h new file mode 100644 index 0000000000..5c35dbd8cc --- /dev/null +++ b/jerry-core/rcs/rcs-recordset.h @@ -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 (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 T read (void); + template 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 */ diff --git a/tests/unit/test_recordset.cpp b/tests/unit/test_recordset.cpp new file mode 100644 index 0000000000..3994d489ec --- /dev/null +++ b/tests/unit/test_recordset.cpp @@ -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 + +#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 (_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 (_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 (rec_p))->get_prev (); + } + case _record_type_two_id: + { + return (static_cast (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 (rec_p))->set_prev (prev_rec_p); + } + case _record_type_two_id: + { + return (static_cast (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 (rec_p))->get_size (); + } + case _record_type_two_id: + { + return (static_cast (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 */