From 528a333e172ca27f613d049f7bbc3957ba8df671 Mon Sep 17 00:00:00 2001 From: Andrey Shitov Date: Wed, 10 Jun 2015 17:06:58 +0300 Subject: [PATCH] Implement recordset iterators JerryScript-DCO-1.0-Signed-off-by: Evgeny Gavrin e.gavrin@samsung.com JerryScript-DCO-1.0-Signed-off-by: Andrey Shitov a.shitov@samsung.com --- jerry-core/rcs/rcs-chunked-list.h | 24 ++++-- jerry-core/rcs/rcs-recordset.cpp | 120 +++++++++++++++++++++++++- jerry-core/rcs/rcs-recordset.h | 134 ++++++++++++++++++++++++++---- tests/unit/test_recordset.cpp | 65 ++++++++++++++- 4 files changed, 315 insertions(+), 28 deletions(-) diff --git a/jerry-core/rcs/rcs-chunked-list.h b/jerry-core/rcs/rcs-chunked-list.h index 18f4a0d88..64c18cfb9 100644 --- a/jerry-core/rcs/rcs-chunked-list.h +++ b/jerry-core/rcs/rcs-chunked-list.h @@ -37,6 +37,14 @@ class rcs_chunked_list_t { public: + /** + * Constructor of chunked list + */ + rcs_chunked_list_t () + { + head_p = tail_p = NULL; + } /* rcs_chunked_list_t */ + /** * List node */ @@ -52,27 +60,27 @@ public: node_t *get_first (void) const; node_t *get_last (void) const; - node_t *get_prev (node_t *node_p) const; + node_t *get_prev (node_t *) const; node_t *get_next (node_t *node_p) const; node_t *append_new (void); - node_t *insert_new (node_t *after_p); + node_t *insert_new (node_t *); - void remove (node_t *node_p); + void remove (node_t *); - node_t *get_node_from_pointer (void *ptr) const; - uint8_t* get_data_space (node_t *node_p) const; + node_t *get_node_from_pointer (void *) const; + uint8_t* get_data_space (node_t *) const; static size_t get_data_space_size (void); private: - void set_prev (node_t *node_p, node_t *prev_node_p); - void set_next (node_t *node_p, node_t *next_node_p); + void set_prev (node_t *, node_t *); + void set_next (node_t *, node_t *); static size_t get_node_size (void); void assert_list_is_correct (void) const; - void assert_node_is_correct (const node_t *node_p) const; + void assert_node_is_correct (const node_t *) const; node_t* head_p; /**< head node of list */ node_t* tail_p; /**< tail node of list */ diff --git a/jerry-core/rcs/rcs-recordset.cpp b/jerry-core/rcs/rcs-recordset.cpp index 2b9fbae60..4bab26b1b 100644 --- a/jerry-core/rcs/rcs-recordset.cpp +++ b/jerry-core/rcs/rcs-recordset.cpp @@ -15,7 +15,6 @@ #include "rcs-chunked-list.h" #include "rcs-recordset.h" -#include "jrt-bit-fields.h" /** \addtogroup recordset Recordset * @{ @@ -91,7 +90,6 @@ rcs_recordset_t::record_t::cpointer_t::decompress (rcs_cpointer_t compressed_poi * compressed pointer */ { uint8_t* base_pointer; - if (compressed_pointer.value.base_cp == MEM_CP_NULL) { base_pointer = NULL; @@ -106,6 +104,19 @@ rcs_recordset_t::record_t::cpointer_t::decompress (rcs_cpointer_t compressed_poi return (rcs_recordset_t::record_t*) (base_pointer + diff); } /* rcs_recordset_t::record_t::cpointer_t::decompress */ +/** + * Create NULL compressed pointer + * + * @return NULL compressed pointer + */ +rcs_cpointer_t +rcs_recordset_t::record_t::cpointer_t::null_cp () +{ + rcs_cpointer_t cp; + cp.packed_value = MEM_CP_NULL; + return cp; +} /* rcs_recordset_t::record_t::cpointer_t::null_cp */ + /** * Assert that 'this' value points to correct record */ @@ -612,6 +623,7 @@ rcs_recordset_t::assert_state_is_correct (void) rec_p != NULL; last_record_p = rec_p, rec_p = next_rec_p) { + JERRY_ASSERT (get_record_size (rec_p) > 0); record_size_sum += get_record_size (rec_p); rcs_chunked_list_t::node_t *node_p = _chunk_list.get_node_from_pointer (rec_p); @@ -649,6 +661,110 @@ rcs_recordset_t::assert_state_is_correct (void) #endif /* !JERRY_NDEBUG */ } /* rcs_recordset_t::assert_state_is_correct */ +/** + * Perform general access to the record + * + * Warning: This function is implemented in assumption that `size` is not more than `2 * node_data_space_size`. + */ +void +rcs_record_iterator_t::access (access_t access_type, /**< type of access: read, write or skip */ + void *data, /**< in/out data to read or write */ + size_t size) /**< size of the data in bytes */ +{ + const size_t node_data_space_size = _recordset_p->_chunk_list.get_data_space_size (); + JERRY_ASSERT (2 * node_data_space_size >= size); + const size_t record_size = _recordset_p->get_record_size (_record_start_p); + + JERRY_ASSERT (!finished ()); + + rcs_chunked_list_t::node_t *current_node_p = _recordset_p->_chunk_list.get_node_from_pointer (_current_pos_p); + uint8_t *current_node_data_space_p = _recordset_p->_chunk_list.get_data_space (current_node_p); + size_t left_in_node = node_data_space_size - (size_t)(_current_pos_p - current_node_data_space_p); + + JERRY_ASSERT (_current_offset + size <= record_size); + + /* + * Read the data and increase the current position pointer. + */ + if (left_in_node >= size) + { + /* all data is placed inside single node */ + if (access_type == ACCESS_READ) + { + memcpy (data, _current_pos_p, size); + } + else if (access_type == ACCESS_WRITE) + { + memcpy (_current_pos_p, data, size); + } + else + { + JERRY_ASSERT (access_type == ACCESS_SKIP); + + if (left_in_node > size) + { + _current_pos_p += size; + } + else if (_current_offset + size < record_size) + { + current_node_p = _recordset_p->_chunk_list.get_next (current_node_p); + JERRY_ASSERT (current_node_p); + _current_pos_p = _recordset_p->_chunk_list.get_data_space (current_node_p); + } + else + { + JERRY_ASSERT (_current_offset + size == record_size); + } + } + } + else + { + /* data is distributed between two nodes */ + size_t first_chunk_size = node_data_space_size - (size_t) (_current_pos_p - current_node_data_space_p); + + if (access_type == ACCESS_READ) + { + memcpy (data, _current_pos_p, first_chunk_size); + } + else if (access_type == ACCESS_WRITE) + { + memcpy (_current_pos_p, data, first_chunk_size); + } + + rcs_chunked_list_t::node_t *next_node_p = _recordset_p->_chunk_list.get_next (current_node_p); + JERRY_ASSERT (next_node_p != NULL); + uint8_t *next_node_data_space_p = _recordset_p->_chunk_list.get_data_space (next_node_p); + + if (access_type == ACCESS_READ) + { + memcpy ((uint8_t *)data + first_chunk_size, next_node_data_space_p, size - first_chunk_size); + } + else if (access_type == ACCESS_WRITE) + { + memcpy (next_node_data_space_p, (uint8_t *)data + first_chunk_size, size - first_chunk_size); + } + else + { + JERRY_ASSERT (access_type == ACCESS_SKIP); + + _current_pos_p = next_node_data_space_p + size - first_chunk_size; + } + } + + /* check if we reached the end */ + if (access_type == ACCESS_SKIP) + { + _current_offset += size; + JERRY_ASSERT (_current_offset <= record_size); + + if (_current_offset == record_size) + { + _current_pos_p = NULL; + _current_offset = 0; + } + } +} /* rcs_record_iterator_t::access */ + /** * Get size of the free record */ diff --git a/jerry-core/rcs/rcs-recordset.h b/jerry-core/rcs/rcs-recordset.h index 2e60358cb..3128d98a3 100644 --- a/jerry-core/rcs/rcs-recordset.h +++ b/jerry-core/rcs/rcs-recordset.h @@ -16,6 +16,8 @@ #ifndef RCS_RECORDSET_H #define RCS_RECORDSET_H +#include + #include "jrt.h" #include "jrt-bit-fields.h" #include "mem-allocator.h" @@ -109,8 +111,12 @@ public: uint16_t packed_value; }; - static cpointer_t compress (record_t* pointer_p); - static record_t* decompress (cpointer_t pointer_cp); + 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: @@ -130,7 +136,7 @@ public: 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; + 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); /** @@ -139,6 +145,9 @@ public: 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; @@ -158,6 +167,7 @@ private: 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 @@ -172,7 +182,7 @@ protected: 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 */ + 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); @@ -180,7 +190,7 @@ protected: 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)); + T *rec_p = static_cast (alloc_space_for_record (size, &prev_rec_p)); rec_p->set_type (type); rec_p->set_size (size); @@ -191,16 +201,14 @@ protected: 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 *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); - 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 void set_prev (record_t *rec_p, record_t *prev_rec_p); - virtual size_t get_record_size (record_t* rec_p); + virtual size_t get_record_size (record_t *rec_p); void assert_state_is_correct (void); }; /* rcs_recordset_t */ @@ -221,17 +229,109 @@ typedef rcs_record_t::cpointer_t rcs_cpointer_t; 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); + /** + * 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: - template T read (void); - template void write (T value); + /** + * 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 */ diff --git a/tests/unit/test_recordset.cpp b/tests/unit/test_recordset.cpp index a3dcbf367..7089e9b54 100644 --- a/tests/unit/test_recordset.cpp +++ b/tests/unit/test_recordset.cpp @@ -41,6 +41,9 @@ extern "C" // Maximum number of elements in a type-one record #define test_max_type_one_record_elements 64 +// Element size in a type-one record +#define test_element_size_type_one_record (sizeof (uint16_t)) + class test_rcs_record_type_one_t : public rcs_record_t { public: @@ -80,7 +83,7 @@ private: 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); + static const size_t element_size = test_element_size_type_one_record; }; class test_rcs_record_type_two_t : public rcs_record_t @@ -232,6 +235,7 @@ main (int __attr_unused___ argc, { test_rcs_record_type_one_t *type_one_records[test_sub_iters]; uint32_t type_one_record_element_counts[test_sub_iters]; + uint16_t type_one_record_elements[test_sub_iters][test_max_type_one_record_elements]; int type_one_records_number = 0; test_rcs_record_type_two_t *type_two_records[test_sub_iters]; @@ -249,6 +253,27 @@ main (int __attr_unused___ argc, JERRY_ASSERT (type_one_records[type_one_records_number] != NULL); + rcs_record_iterator_t it (&storage, type_one_records[type_one_records_number]); + it.skip (); // skip header + it.skip (); // skip header + for (uint32_t i = 0; i < type_one_record_element_counts[type_one_records_number]; it.skip(), i++) + { + uint16_t val = (uint16_t)rand (); + type_one_record_elements[type_one_records_number][i] = val; + it.write (val); + } + + JERRY_ASSERT (type_one_records[type_one_records_number] != NULL); + + it.reset (); + it.skip (); // skip header + it.skip (); // skip header + for (uint32_t i = 0; i < type_one_record_element_counts[type_one_records_number]; it.skip(), i++) + { + uint16_t val = type_one_record_elements[type_one_records_number][i]; + JERRY_ASSERT (val == it.read ()); + } + type_one_records_number++; } else @@ -265,6 +290,26 @@ main (int __attr_unused___ argc, while (type_one_records_number + type_two_records_number != 0) { + // Read test + for (int index_to_free = 0; index_to_free < type_one_records_number; index_to_free++) + { + JERRY_ASSERT (type_one_records_number > 0); + + JERRY_ASSERT (index_to_free >= 0 && index_to_free < type_one_records_number); + + rcs_record_iterator_t it (&storage, type_one_records[index_to_free]); + it.skip (); // skip header + it.skip (); // skip header + for (uint32_t i = 0; i < type_one_record_element_counts[index_to_free]; it.skip(), i++) + { + uint16_t val = type_one_record_elements[index_to_free][i]; + JERRY_ASSERT (it.read () == val); + } + + JERRY_ASSERT (JERRY_ALIGNUP (type_one_record_element_counts[index_to_free]*2 + 8, RCS_DYN_STORAGE_ALIGNMENT) == + type_one_records[index_to_free]->get_size ()); + } + bool free_type_one; if (type_one_records_number == 0) @@ -291,6 +336,16 @@ main (int __attr_unused___ argc, JERRY_ASSERT (index_to_free >= 0 && index_to_free < type_one_records_number); + rcs_record_iterator_t it (&storage, type_one_records[index_to_free]); + it.skip (); // skip header + it.skip (); // skip header + for (uint32_t i = 0; i < type_one_record_element_counts[index_to_free]; it.skip(), i++) + { + uint16_t val = type_one_record_elements[index_to_free][i]; + JERRY_ASSERT (it.read () == val); + } + + // free the record storage.free_record_type_one (type_one_records[index_to_free]); type_one_records_number--; @@ -298,6 +353,8 @@ main (int __attr_unused___ argc, { 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]; + memcpy (type_one_record_elements[index_to_free], type_one_record_elements[index_to_free + 1], + test_max_type_one_record_elements * test_element_size_type_one_record); index_to_free++; } @@ -328,3 +385,9 @@ main (int __attr_unused___ argc, return 0; } /* main */ + +template test_rcs_record_type_one_t * +rcs_recordset_t::alloc_record (rcs_record_t::type_t, uint32_t); + +template test_rcs_record_type_two_t * +rcs_recordset_t::alloc_record (rcs_record_t::type_t);