/* 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"

/** \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 */

/**
 * 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
 */
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_DISABLE_HEAVY_DEBUG
  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)
  {
    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);

    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_DISABLE_HEAVY_DEBUG */
} /* 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
 */
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 */

/**
 * @}
 */
