ecma_string_get_chars (const ecma_string_t *string_p, /**< ecma-string */
                       lit_utf8_size_t *size_p, /**< [out] size of the ecma string */
                       lit_utf8_size_t *length_p, /**< [out] optional argument. If the pointer is not NULL the pointed
                                                   *    memory area is filled with the length of the ecma string */
                       lit_utf8_byte_t *uint32_buff_p, /**< [out] optional argument. If the pointer is not NULL the
                                                        *    pointed memory area is filled with the string converted
                                                        *    uint32 string descriptor */
                       uint8_t *flags_p) /**< [in,out] any combination of ecma_string_flag_t bits */
{
  lit_utf8_size_t length;
  lit_utf8_size_t size;
  const lit_utf8_byte_t *result_p;

  if (ECMA_IS_DIRECT_STRING (string_p))
  {
    *flags_p |= ECMA_STRING_FLAG_REHASH_NEEDED;

    switch (ECMA_GET_DIRECT_STRING_TYPE (string_p))
    {
      case ECMA_DIRECT_STRING_MAGIC:
      {
        uint32_t id = (uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p);

        if (id >= LIT_MAGIC_STRING__COUNT)
        {
          id -= LIT_MAGIC_STRING__COUNT;
          size = lit_get_magic_string_ex_size (id);
          result_p = lit_get_magic_string_ex_utf8 (id);
          length = 0;

          if (JERRY_UNLIKELY (*flags_p & ECMA_STRING_FLAG_IS_ASCII))
          {
            length = lit_utf8_string_length (result_p, size);
          }
        }
        else
        {
          size = lit_get_magic_string_size (id);
          length = size;

          result_p = lit_get_magic_string_utf8 (id);

          /* All magic strings must be ascii strings. */
          JERRY_ASSERT (ECMA_STRING_IS_ASCII (result_p, size));
        }
        break;
      }
      default:
      {
        JERRY_ASSERT (ECMA_GET_DIRECT_STRING_TYPE (string_p) == ECMA_DIRECT_STRING_UINT);
        uint32_t uint32_number = (uint32_t) ECMA_GET_DIRECT_STRING_VALUE (string_p);
        size = (lit_utf8_size_t) ecma_string_get_uint32_size (uint32_number);

        if (uint32_buff_p != NULL)
        {
          result_p = uint32_buff_p;
        }
        else
        {
          result_p = (const lit_utf8_byte_t *) jmem_heap_alloc_block (size);
          *flags_p |= ECMA_STRING_FLAG_MUST_BE_FREED;
        }

        length = ecma_uint32_to_utf8_string (uint32_number, (lit_utf8_byte_t *) result_p, size);

        JERRY_ASSERT (length == size);
        *flags_p |= ECMA_STRING_FLAG_IS_UINT32;
        break;
      }
    }
  }
  else
  {
    JERRY_ASSERT (string_p->refs_and_container >= ECMA_STRING_REF_ONE);

    switch (ECMA_STRING_GET_CONTAINER (string_p))
    {
      case ECMA_STRING_CONTAINER_HEAP_UTF8_STRING:
      {
        ecma_short_string_t *short_string_p = (ecma_short_string_t *) string_p;
        size = short_string_p->size;
        length = short_string_p->length;
        result_p = ECMA_SHORT_STRING_GET_BUFFER (short_string_p);
        break;
      }
      case ECMA_STRING_CONTAINER_LONG_OR_EXTERNAL_STRING:
      {
        ecma_long_string_t *long_string_desc_p = (ecma_long_string_t *) string_p;
        size = long_string_desc_p->size;
        length = long_string_desc_p->length;
        result_p = long_string_desc_p->string_p;
        break;
      }
      case ECMA_STRING_CONTAINER_HEAP_ASCII_STRING:
      {
        size = ECMA_ASCII_STRING_GET_SIZE (string_p);
        length = size;
        result_p = ECMA_ASCII_STRING_GET_BUFFER (string_p);
        break;
      }
      case ECMA_STRING_CONTAINER_UINT32_IN_DESC:
      {
        size = (lit_utf8_size_t) ecma_string_get_uint32_size (string_p->u.uint32_number);

        if (uint32_buff_p != NULL)
        {
          result_p = uint32_buff_p;
        }
        else
        {
          result_p = (const lit_utf8_byte_t *) jmem_heap_alloc_block (size);
          *flags_p |= ECMA_STRING_FLAG_MUST_BE_FREED;
        }

        length = ecma_uint32_to_utf8_string (string_p->u.uint32_number, (lit_utf8_byte_t *) result_p, size);

        JERRY_ASSERT (length == size);
        *flags_p |= ECMA_STRING_FLAG_IS_UINT32 | ECMA_STRING_FLAG_REHASH_NEEDED;
        break;

      }
      default:
      {
        JERRY_ASSERT (ECMA_STRING_GET_CONTAINER (string_p) == ECMA_STRING_CONTAINER_MAGIC_STRING_EX);

        lit_magic_string_ex_id_t id = LIT_MAGIC_STRING__COUNT - string_p->u.magic_string_ex_id;
        size = lit_get_magic_string_ex_size (id);
        length = 0;

        if (JERRY_UNLIKELY (*flags_p & ECMA_STRING_FLAG_IS_ASCII))
        {
          length = lit_utf8_string_length (lit_get_magic_string_ex_utf8 (id), size);
        }

        result_p = lit_get_magic_string_ex_utf8 (id);
        *flags_p |= ECMA_STRING_FLAG_REHASH_NEEDED;
        break;
      }
    }
  }

  *size_p = size;
  if (length_p != NULL)
  {
    *length_p = length;
  }

  if ((*flags_p & ECMA_STRING_FLAG_IS_ASCII)
      && length != size)
  {
    *flags_p = (uint8_t) (*flags_p & (uint8_t) ~ECMA_STRING_FLAG_IS_ASCII);
  }

  return result_p;
} /* ecma_string_get_chars */