/* 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 #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 */ /* Free memory occupied by the dynamic storage */ void cleanup (void) { _chunk_list.cleanup (); } /* cleanup */ /** * 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); static cpointer_t null_cp (); static const int conval = 3; }; 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; }; record_t *get_first (void); record_t *get_next (record_t *rec_p); 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); virtual record_t *get_prev (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: /** * Constructor */ rcs_record_iterator_t (rcs_recordset_t *rcs_p, /**< recordset */ rcs_record_t *rec_p) /**< record which should belong to the recordset */ { _record_start_p = rec_p; _recordset_p = rcs_p; reset (); } /* rcs_record_iterator_t */ /** * Constructor */ rcs_record_iterator_t (rcs_recordset_t *rcs_p, /**< recordset */ rcs_cpointer_t rec_ext_cp) /**< compressed pointer to the record */ { _record_start_p = rcs_cpointer_t::decompress (rec_ext_cp); _recordset_p = rcs_p; reset (); } /* rcs_record_iterator_t */ protected: /** * Types of access */ typedef enum { ACCESS_WRITE, /**< If access_type == ACCESS_WRITE, * write 'size' bytes from 'data' buffer to the record. */ ACCESS_READ, /**< If access_type == ACCESS_READ, * read 'size' bytes from the record and write to the 'data' buffer. */ ACCESS_SKIP /**< If access_type == ACCESS_SKIP, * increment current position so that 'size' bytes would be skipped. */ } access_t; void access (access_t access_type, void *data, size_t size); public: /** * Read value of type T from the record. * After reading iterator doesn't change its position. * * @return read value */ template T read (void) { T data; access (ACCESS_READ, &data, sizeof (T)); return data; } /* read */ /** * Write value of type T to the record. * After writing iterator doesn't change its position. */ template void write (T value) /**< value to write */ { access (ACCESS_WRITE, &value, sizeof (T)); } /* write */ /** * Increment current position to skip T value in the record. */ template void skip () { access (ACCESS_SKIP, NULL, sizeof (T)); } /* skip */ /** * Increment current position to skip 'size' bytes. */ void skip (size_t size) /**< number of bytes to skip */ { access (ACCESS_SKIP, NULL, size); } /* skip */ /** * Check if the end of the record was reached. * * @return true if the whole record was iterated * false otherwise */ bool finished () { return _current_pos_p == NULL; } /* finished */ /** * Reset the iterator, so that it points to the beginning of the record */ void reset () { _current_pos_p = (uint8_t *)_record_start_p; _current_offset = 0; } /* reset */ private: rcs_record_t* _record_start_p; /**< start of current record */ uint8_t* _current_pos_p; /**< pointer to current offset in current record */ size_t _current_offset; /**< current offset */ 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 */