Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * This file is part of libdom.
  3.  * Licensed under the MIT License,
  4.  *                http://www.opensource.org/licenses/mit-license.php
  5.  * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
  6.  * Copyright 2009 Bo Yang <struggleyb.nku@gmail.com>
  7.  */
  8.  
  9. #include <assert.h>
  10. #include <ctype.h>
  11. #include <inttypes.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14.  
  15. typedef signed char int8_t;
  16. typedef signed short int16_t;
  17. typedef signed int int32_t;
  18.  
  19. typedef unsigned char uint8_t;
  20. typedef unsigned short uint16_t;
  21. typedef unsigned int uint32_t;
  22.  
  23.  
  24. #include <parserutils/charset/utf8.h>
  25.  
  26. #include "core/string.h"
  27. #include "core/document.h"
  28. #include "utils/utils.h"
  29.  
  30. /**
  31.  * Type of a DOM string
  32.  */
  33. enum dom_string_type {
  34.         DOM_STRING_CDATA = 0,
  35.         DOM_STRING_INTERNED = 1
  36. };
  37.  
  38. /**
  39.  * A DOM string
  40.  *
  41.  * Strings are reference counted so destruction is performed correctly.
  42.  */
  43. typedef struct dom_string_internal {
  44.         dom_string base;
  45.  
  46.         union {
  47.                 struct {
  48.                         uint8_t *ptr;   /**< Pointer to string data */
  49.                         size_t len;     /**< Byte length of string */
  50.                 } cdata;
  51.                 lwc_string *intern;     /**< Interned string */
  52.         } data;
  53.  
  54.         enum dom_string_type type;      /**< String type */
  55. } dom_string_internal;
  56.  
  57. /**
  58.  * Empty string, for comparisons against NULL
  59.  */
  60. static const dom_string_internal empty_string = {
  61.         { 0 },
  62.         { { (uint8_t *) "", 0 } },
  63.         DOM_STRING_CDATA
  64. };
  65.  
  66. void dom_string_destroy(dom_string *str)
  67. {
  68.         dom_string_internal *istr = (dom_string_internal *)str;
  69.         if (str != NULL) {
  70.                 assert(istr->base.refcnt == 0);
  71.                 switch (istr->type) {
  72.                 case DOM_STRING_INTERNED:
  73.                         if (istr->data.intern != NULL) {
  74.                                 lwc_string_unref(istr->data.intern);
  75.                         }
  76.                         break;
  77.                 case DOM_STRING_CDATA:
  78.                         free(istr->data.cdata.ptr);
  79.                         break;
  80.                 }
  81.  
  82.                 free(str);
  83.         }
  84. }
  85.  
  86. /**
  87.  * Create a DOM string from a string of characters
  88.  *
  89.  * \param ptr    Pointer to string of characters
  90.  * \param len    Length, in bytes, of string of characters
  91.  * \param str    Pointer to location to receive result
  92.  * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion
  93.  *
  94.  * The returned string will already be referenced, so there is no need
  95.  * to explicitly reference it.
  96.  *
  97.  * The string of characters passed in will be copied for use by the
  98.  * returned DOM string.
  99.  */
  100. dom_exception dom_string_create(const uint8_t *ptr, size_t len,
  101.                 dom_string **str)
  102. {
  103.         dom_string_internal *ret;
  104.  
  105.         if (ptr == NULL || len == 0) {
  106.                 ptr = (const uint8_t *) "";
  107.                 len = 0;
  108.         }
  109.  
  110.         ret = malloc(sizeof(*ret));
  111.         if (ret == NULL)
  112.                 return DOM_NO_MEM_ERR;
  113.  
  114.         ret->data.cdata.ptr = malloc(len + 1);
  115.         if (ret->data.cdata.ptr == NULL) {
  116.                 free(ret);
  117.                 return DOM_NO_MEM_ERR;
  118.         }
  119.  
  120.         memcpy(ret->data.cdata.ptr, ptr, len);
  121.         ret->data.cdata.ptr[len] = '\0';
  122.  
  123.         ret->data.cdata.len = len;
  124.  
  125.         ret->base.refcnt = 1;
  126.  
  127.         ret->type = DOM_STRING_CDATA;
  128.  
  129.         *str = (dom_string *)ret;
  130.  
  131.         return DOM_NO_ERR;
  132. }
  133.  
  134. /**
  135.  * Create an interned DOM string from a string of characters
  136.  *
  137.  * \param ptr    Pointer to string of characters
  138.  * \param len    Length, in bytes, of string of characters
  139.  * \param str    Pointer to location to receive result
  140.  * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion
  141.  *
  142.  * The returned string will already be referenced, so there is no need
  143.  * to explicitly reference it.
  144.  *
  145.  * The string of characters passed in will be copied for use by the
  146.  * returned DOM string.
  147.  */
  148. dom_exception dom_string_create_interned(const uint8_t *ptr, size_t len,
  149.                 dom_string **str)
  150. {
  151.         dom_string_internal *ret;
  152.  
  153.         if (ptr == NULL || len == 0) {
  154.                 ptr = (const uint8_t *) "";
  155.                 len = 0;
  156.         }
  157.  
  158.         ret = malloc(sizeof(*ret));
  159.         if (ret == NULL)
  160.                 return DOM_NO_MEM_ERR;
  161.  
  162.         if (lwc_intern_string((const char *) ptr, len,
  163.                         &ret->data.intern) != lwc_error_ok) {
  164.                 free(ret);
  165.                 return DOM_NO_MEM_ERR;
  166.         }
  167.  
  168.         ret->base.refcnt = 1;
  169.  
  170.         ret->type = DOM_STRING_INTERNED;
  171.  
  172.         *str = (dom_string *)ret;
  173.  
  174.         return DOM_NO_ERR;
  175. }
  176.  
  177. /**
  178.  * Make the dom_string be interned
  179.  *
  180.  * \param str     The dom_string to be interned
  181.  * \param lwcstr  The result lwc_string
  182.  * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
  183.  */
  184. dom_exception dom_string_intern(dom_string *str,
  185.                 struct lwc_string_s **lwcstr)
  186. {
  187.         dom_string_internal *istr = (dom_string_internal *) str;
  188.         /* If this string is already interned, do nothing */
  189.         if (istr->type != DOM_STRING_INTERNED) {
  190.                 lwc_string *ret;
  191.                 lwc_error lerr;
  192.  
  193.                 lerr = lwc_intern_string((const char *) istr->data.cdata.ptr,
  194.                                 istr->data.cdata.len, &ret);
  195.                 if (lerr != lwc_error_ok) {
  196.                         return _dom_exception_from_lwc_error(lerr);
  197.                 }
  198.  
  199.                 free(istr->data.cdata.ptr);
  200.  
  201.                 istr->data.intern = ret;
  202.  
  203.                 istr->type = DOM_STRING_INTERNED;
  204.         }
  205.  
  206.         *lwcstr = lwc_string_ref(istr->data.intern);
  207.  
  208.         return DOM_NO_ERR;
  209. }
  210.  
  211. /**
  212.  * Case sensitively compare two DOM strings
  213.  *
  214.  * \param s1  The first string to compare
  215.  * \param s2  The second string to compare
  216.  * \return true if strings match, false otherwise
  217.  */
  218. bool dom_string_isequal(const dom_string *s1, const dom_string *s2)
  219. {
  220.         size_t len;
  221.         const dom_string_internal *is1 = (dom_string_internal *) s1;
  222.         const dom_string_internal *is2 = (dom_string_internal *) s2;
  223.  
  224.         if (s1 == NULL)
  225.                 is1 = &empty_string;
  226.  
  227.         if (s2 == NULL)
  228.                 is2 = &empty_string;
  229.  
  230.         if (is1->type == DOM_STRING_INTERNED &&
  231.                         is2->type == DOM_STRING_INTERNED) {
  232.                 bool match;
  233.  
  234.                 (void) lwc_string_isequal(is1->data.intern, is2->data.intern,
  235.                         &match);
  236.  
  237.                 return match;
  238.         }
  239.  
  240.         len = dom_string_byte_length((dom_string *) is1);
  241.  
  242.         if (len != dom_string_byte_length((dom_string *)is2))
  243.                 return false;
  244.  
  245.         return 0 == memcmp(dom_string_data((dom_string *) is1), dom_string_data((dom_string *)is2), len);
  246. }
  247.  
  248. /**
  249.  * Trivial locale-agnostic lower case convertor
  250.  */
  251. static inline uint8_t dolower(const uint8_t c)
  252. {
  253.         if ('A' <= c && c <= 'Z')
  254.                 return c + 'a' - 'A';
  255.         return c;
  256. }
  257.  
  258. /**
  259.  * Case insensitively compare two DOM strings
  260.  *
  261.  * \param s1  The first string to compare
  262.  * \param s2  The second string to compare
  263.  * \return true if strings match, false otherwise
  264.  */
  265. bool dom_string_caseless_isequal(const dom_string *s1, const dom_string *s2)
  266. {
  267.         const uint8_t *d1 = NULL;
  268.         const uint8_t *d2 = NULL;
  269.         size_t len;
  270.         const dom_string_internal *is1 = (dom_string_internal *) s1;
  271.         const dom_string_internal *is2 = (dom_string_internal *) s2;
  272.  
  273.         if (s1 == NULL)
  274.                 is1 = &empty_string;
  275.  
  276.         if (s2 == NULL)
  277.                 is2 = &empty_string;
  278.  
  279.         if (is1->type == DOM_STRING_INTERNED &&
  280.                         is2->type == DOM_STRING_INTERNED) {
  281.                 bool match;
  282.  
  283.                 if (lwc_string_caseless_isequal(is1->data.intern, is2->data.intern,
  284.                                                 &match) != lwc_error_ok)
  285.                         return false;
  286.  
  287.                 return match;
  288.         }
  289.  
  290.         len = dom_string_byte_length((dom_string *) is1);
  291.  
  292.         if (len != dom_string_byte_length((dom_string *)is2))
  293.                 return false;
  294.  
  295.         d1 = (const uint8_t *) dom_string_data((dom_string *) is1);
  296.         d2 = (const uint8_t *) dom_string_data((dom_string *)is2);
  297.  
  298.         while (len > 0) {
  299.                 if (dolower(*d1) != dolower(*d2))
  300.                         return false;
  301.  
  302.                 d1++;
  303.                 d2++;
  304.                 len--;
  305.         }
  306.  
  307.         return true;
  308. }
  309.  
  310.  
  311. /**
  312.  * Case sensitively compare DOM string with lwc_string
  313.  *
  314.  * \param s1  The first string to compare
  315.  * \param s2  The second string to compare
  316.  * \return true if strings match, false otherwise
  317.  *
  318.  * Returns false if either are NULL.
  319.  */
  320. bool dom_string_lwc_isequal(const dom_string *s1, lwc_string *s2)
  321. {
  322.         size_t len;
  323.         dom_string_internal *is1 = (dom_string_internal *) s1;
  324.  
  325.         if (s1 == NULL || s2 == NULL)
  326.                 return false;
  327.  
  328.         if (is1->type == DOM_STRING_INTERNED) {
  329.                 bool match;
  330.  
  331.                 (void) lwc_string_isequal(is1->data.intern, s2, &match);
  332.  
  333.                 return match;
  334.         }
  335.  
  336.         /* Handle non-interned case */
  337.         len = dom_string_byte_length(s1);
  338.  
  339.         if (len != lwc_string_length(s2))
  340.                 return false;
  341.  
  342.         return 0 == memcmp(dom_string_data(s1), lwc_string_data(s2), len);
  343. }
  344.  
  345.  
  346. /**
  347.  * Case insensitively compare DOM string with lwc_string
  348.  *
  349.  * \param s1  The first string to compare
  350.  * \param s2  The second string to compare
  351.  * \return true if strings match, false otherwise
  352.  *
  353.  * Returns false if either are NULL.
  354.  */
  355. bool dom_string_caseless_lwc_isequal(const dom_string *s1, lwc_string *s2)
  356. {
  357.         size_t len;
  358.         const uint8_t *d1 = NULL;
  359.         const uint8_t *d2 = NULL;
  360.         dom_string_internal *is1 = (dom_string_internal *) s1;
  361.  
  362.         if (s1 == NULL || s2 == NULL)
  363.                 return false;
  364.  
  365.         if (is1->type == DOM_STRING_INTERNED) {
  366.                 bool match;
  367.  
  368.                 if (lwc_string_caseless_isequal(is1->data.intern, s2, &match) != lwc_error_ok)
  369.                         return false;
  370.  
  371.                 return match;
  372.         }
  373.  
  374.         len = dom_string_byte_length(s1);
  375.  
  376.         if (len != lwc_string_length(s2))
  377.                 return false;
  378.  
  379.         d1 = (const uint8_t *) dom_string_data(s1);
  380.         d2 = (const uint8_t *) lwc_string_data(s2);
  381.  
  382.         while (len > 0) {
  383.                 if (dolower(*d1) != dolower(*d2))
  384.                         return false;
  385.  
  386.                 d1++;
  387.                 d2++;
  388.                 len--;
  389.         }
  390.  
  391.         return true;
  392. }
  393.  
  394.  
  395. /**
  396.  * Get the index of the first occurrence of a character in a dom string
  397.  *
  398.  * \param str  The string to search in
  399.  * \param chr  UCS4 value to look for
  400.  * \return Character index of found character, or -1 if none found
  401.  */
  402. uint32_t dom_string_index(dom_string *str, uint32_t chr)
  403. {
  404.         const uint8_t *s;
  405.         size_t clen, slen;
  406.         uint32_t c, index;
  407.         parserutils_error err;
  408.  
  409.         s = (const uint8_t *) dom_string_data(str);
  410.         slen = dom_string_byte_length(str);
  411.  
  412.         index = 0;
  413.  
  414.         while (slen > 0) {
  415.                 err = parserutils_charset_utf8_to_ucs4(s, slen, &c, &clen);
  416.                 if (err != PARSERUTILS_OK) {
  417.                         return (uint32_t) -1;
  418.                 }
  419.  
  420.                 if (c == chr) {
  421.                         return index;
  422.                 }
  423.  
  424.                 s += clen;
  425.                 slen -= clen;
  426.                 index++;
  427.         }
  428.  
  429.         return (uint32_t) -1;
  430. }
  431.  
  432. /**
  433.  * Get the index of the last occurrence of a character in a dom string
  434.  *
  435.  * \param str  The string to search in
  436.  * \param chr  UCS4 value to look for
  437.  * \return Character index of found character, or -1 if none found
  438.  */
  439. uint32_t dom_string_rindex(dom_string *str, uint32_t chr)
  440. {
  441.         const uint8_t *s;
  442.         size_t clen = 0, slen;
  443.         uint32_t c, coff, index;
  444.         parserutils_error err;
  445.  
  446.         s = (const uint8_t *) dom_string_data(str);
  447.         slen = dom_string_byte_length(str);
  448.  
  449.         index = dom_string_length(str);
  450.  
  451.         while (slen > 0) {
  452.                 err = parserutils_charset_utf8_prev(s, slen,
  453.                                 (uint32_t *) &coff);
  454.                 if (err == PARSERUTILS_OK) {
  455.                         err = parserutils_charset_utf8_to_ucs4(s + coff,
  456.                                         slen - clen, &c, &clen);
  457.                 }
  458.  
  459.                 if (err != PARSERUTILS_OK) {
  460.                         return (uint32_t) -1;
  461.                 }
  462.  
  463.                 if (c == chr) {
  464.                         return index;
  465.                 }
  466.  
  467.                 slen -= clen;
  468.                 index--;
  469.         }
  470.  
  471.         return (uint32_t) -1;
  472. }
  473.  
  474. /**
  475.  * Get the length, in characters, of a dom string
  476.  *
  477.  * \param str  The string to measure the length of
  478.  * \return The length of the string, in characters
  479.  */
  480. uint32_t dom_string_length(dom_string *str)
  481. {
  482.         const uint8_t *s;
  483.         size_t slen, clen;
  484.         parserutils_error err;
  485.  
  486.         s = (const uint8_t *) dom_string_data(str);
  487.         slen = dom_string_byte_length(str);
  488.  
  489.         err = parserutils_charset_utf8_length(s, slen, &clen);
  490.         if (err != PARSERUTILS_OK) {
  491.                 return 0;
  492.         }
  493.  
  494.         return clen;
  495. }
  496.  
  497. /**
  498.  * Get the UCS4 character at position index
  499.  *
  500.  * \param index  The position of the charater
  501.  * \param ch     The UCS4 character
  502.  * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
  503.  */
  504. dom_exception dom_string_at(dom_string *str, uint32_t index,
  505.                 uint32_t *ch)
  506. {
  507.         const uint8_t *s;
  508.         size_t clen, slen;
  509.         uint32_t c, i;
  510.         parserutils_error err;
  511.  
  512.         s = (const uint8_t *) dom_string_data(str);
  513.         slen = dom_string_byte_length(str);
  514.  
  515.         i = 0;
  516.  
  517.         while (slen > 0) {
  518.                 err = parserutils_charset_utf8_char_byte_length(s, &clen);
  519.                 if (err != PARSERUTILS_OK) {
  520.                         return (uint32_t) -1;
  521.                 }
  522.  
  523.                 i++;
  524.                 if (i == index + 1)
  525.                         break;
  526.  
  527.                 s += clen;
  528.                 slen -= clen;
  529.         }
  530.  
  531.         if (i == index + 1) {
  532.                 err = parserutils_charset_utf8_to_ucs4(s, slen, &c, &clen);
  533.                 if (err != PARSERUTILS_OK) {
  534.                         return (uint32_t) -1;
  535.                 }
  536.  
  537.                 *ch = c;
  538.                 return DOM_NO_ERR;
  539.         } else {
  540.                 return DOM_DOMSTRING_SIZE_ERR;
  541.         }
  542. }
  543.  
  544. /**
  545.  * Concatenate two dom strings
  546.  *
  547.  * \param s1      The first string
  548.  * \param s2      The second string
  549.  * \param result  Pointer to location to receive result
  550.  * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion
  551.  *
  552.  * The returned string will be referenced. The client
  553.  * should dereference it once it has finished with it.
  554.  */
  555. dom_exception dom_string_concat(dom_string *s1, dom_string *s2,
  556.                 dom_string **result)
  557. {
  558.         dom_string_internal *concat;
  559.         const uint8_t *s1ptr, *s2ptr;
  560.         size_t s1len, s2len;
  561.  
  562.         assert(s1 != NULL);
  563.         assert(s2 != NULL);
  564.  
  565.         s1ptr = (const uint8_t *) dom_string_data(s1);
  566.         s2ptr = (const uint8_t *) dom_string_data(s2);
  567.         s1len = dom_string_byte_length(s1);
  568.         s2len = dom_string_byte_length(s2);
  569.  
  570.         concat = malloc(sizeof(*concat));
  571.         if (concat == NULL) {
  572.                 return DOM_NO_MEM_ERR;
  573.         }
  574.  
  575.         concat->data.cdata.ptr = malloc(s1len + s2len + 1);
  576.         if (concat->data.cdata.ptr == NULL) {
  577.                 free(concat);
  578.  
  579.                 return DOM_NO_MEM_ERR;
  580.         }
  581.  
  582.         memcpy(concat->data.cdata.ptr, s1ptr, s1len);
  583.  
  584.         memcpy(concat->data.cdata.ptr + s1len, s2ptr, s2len);
  585.  
  586.         concat->data.cdata.ptr[s1len + s2len] = '\0';
  587.  
  588.         concat->data.cdata.len = s1len + s2len;
  589.  
  590.         concat->base.refcnt = 1;
  591.  
  592.         concat->type = DOM_STRING_CDATA;
  593.  
  594.         *result = (dom_string *)concat;
  595.  
  596.         return DOM_NO_ERR;
  597. }
  598.  
  599. /**
  600.  * Extract a substring from a dom string
  601.  *
  602.  * \param str     The string to extract from
  603.  * \param i1      The character index of the start of the substring
  604.  * \param i2      The character index of the end of the substring
  605.  * \param result  Pointer to location to receive result
  606.  * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion
  607.  *
  608.  * The returned string will have its reference count increased. The client
  609.  * should dereference it once it has finished with it.
  610.  */
  611. dom_exception dom_string_substr(dom_string *str,
  612.                 uint32_t i1, uint32_t i2, dom_string **result)
  613. {
  614.         const uint8_t *s = (const uint8_t *) dom_string_data(str);
  615.         size_t slen = dom_string_byte_length(str);
  616.         uint32_t b1, b2;
  617.         parserutils_error err;
  618.  
  619.         /* Initialise the byte index of the start to 0 */
  620.         b1 = 0;
  621.         /* Make the end a character offset from the start */
  622.         i2 -= i1;
  623.  
  624.         /* Calculate the byte index of the start */
  625.         while (i1 > 0) {
  626.                 err = parserutils_charset_utf8_next(s, slen, b1, &b1);
  627.                 if (err != PARSERUTILS_OK) {
  628.                         return DOM_NO_MEM_ERR;
  629.                 }
  630.  
  631.                 i1--;
  632.         }
  633.  
  634.         /* Initialise the byte index of the end to that of the start */
  635.         b2 = b1;
  636.  
  637.         /* Calculate the byte index of the end */
  638.         while (i2 > 0) {
  639.                 err = parserutils_charset_utf8_next(s, slen, b2, &b2);
  640.                 if (err != PARSERUTILS_OK) {
  641.                         return DOM_NO_MEM_ERR;
  642.                 }
  643.  
  644.                 i2--;
  645.         }
  646.  
  647.         /* Create a string from the specified byte range */
  648.         return dom_string_create(s + b1, b2 - b1, result);
  649. }
  650.  
  651. /**
  652.  * Insert data into a dom string at the given location
  653.  *
  654.  * \param target  Pointer to string to insert into
  655.  * \param source  Pointer to string to insert
  656.  * \param offset  Character offset of location to insert at
  657.  * \param result  Pointer to location to receive result
  658.  * \return DOM_NO_ERR          on success,
  659.  *         DOM_NO_MEM_ERR      on memory exhaustion,
  660.  *         DOM_INDEX_SIZE_ERR  if ::offset > len(::target).
  661.  *
  662.  * The returned string will have its reference count increased. The client
  663.  * should dereference it once it has finished with it.
  664.  */
  665. dom_exception dom_string_insert(dom_string *target,
  666.                 dom_string *source, uint32_t offset,
  667.                 dom_string **result)
  668. {
  669.         dom_string_internal *res;
  670.         const uint8_t *t, *s;
  671.         uint32_t tlen, slen, clen;
  672.         uint32_t ins = 0;
  673.         parserutils_error err;
  674.  
  675.         t = (const uint8_t *) dom_string_data(target);
  676.         tlen = dom_string_byte_length(target);
  677.         s = (const uint8_t *) dom_string_data(source);
  678.         slen = dom_string_byte_length(source);
  679.  
  680.         clen = dom_string_length(target);
  681.  
  682.         if (offset > clen)
  683.                 return DOM_INDEX_SIZE_ERR;
  684.  
  685.         /* Calculate the byte index of the insertion point */
  686.         if (offset == clen) {
  687.                 /* Optimisation for append */
  688.                 ins = tlen;
  689.         } else {
  690.                 while (offset > 0) {
  691.                         err = parserutils_charset_utf8_next(t, tlen,
  692.                                         ins, &ins);
  693.  
  694.                         if (err != PARSERUTILS_OK) {
  695.                                 return DOM_NO_MEM_ERR;
  696.                         }
  697.  
  698.                         offset--;
  699.                 }
  700.         }
  701.  
  702.         /* Allocate result string */
  703.         res = malloc(sizeof(*res));
  704.         if (res == NULL) {
  705.                 return DOM_NO_MEM_ERR;
  706.         }
  707.  
  708.         /* Allocate data buffer for result contents */
  709.         res->data.cdata.ptr = malloc(tlen + slen + 1);
  710.         if (res->data.cdata.ptr == NULL) {
  711.                 free(res);
  712.                 return DOM_NO_MEM_ERR;
  713.         }
  714.  
  715.         /* Copy initial portion of target, if any, into result */
  716.         if (ins > 0) {
  717.                 memcpy(res->data.cdata.ptr, t, ins);
  718.         }
  719.  
  720.         /* Copy inserted data into result */
  721.         memcpy(res->data.cdata.ptr + ins, s, slen);
  722.  
  723.         /* Copy remainder of target, if any, into result */
  724.         if (tlen - ins > 0) {
  725.                 memcpy(res->data.cdata.ptr + ins + slen, t + ins, tlen - ins);
  726.         }
  727.  
  728.         res->data.cdata.ptr[tlen + slen] = '\0';
  729.  
  730.         res->data.cdata.len = tlen + slen;
  731.  
  732.         res->base.refcnt = 1;
  733.  
  734.         res->type = DOM_STRING_CDATA;
  735.  
  736.         *result = (dom_string *)res;
  737.  
  738.         return DOM_NO_ERR;
  739. }
  740.  
  741. /**
  742.  * Replace a section of a dom string
  743.  *
  744.  * \param target  Pointer to string of which to replace a section
  745.  * \param source  Pointer to replacement string
  746.  * \param i1      Character index of start of region to replace
  747.  * \param i2      Character index of end of region to replace
  748.  * \param result  Pointer to location to receive result
  749.  * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion.
  750.  *
  751.  * The returned string will have its reference count increased. The client
  752.  * should dereference it once it has finished with it.
  753.  */
  754. dom_exception dom_string_replace(dom_string *target,
  755.                 dom_string *source, uint32_t i1, uint32_t i2,
  756.                 dom_string **result)
  757. {
  758.         dom_string_internal *res;
  759.         const uint8_t *t, *s;
  760.         uint32_t tlen, slen;
  761.         uint32_t b1, b2;
  762.         parserutils_error err;
  763.  
  764.         t = (const uint8_t *) dom_string_data(target);
  765.         tlen = dom_string_byte_length(target);
  766.         s = (const uint8_t *) dom_string_data(source);
  767.         slen = dom_string_byte_length(source);
  768.  
  769.         /* Initialise the byte index of the start to 0 */
  770.         b1 = 0;
  771.         /* Make the end a character offset from the start */
  772.         i2 -= i1;
  773.  
  774.         /* Calculate the byte index of the start */
  775.         while (i1 > 0) {
  776.                 err = parserutils_charset_utf8_next(t, tlen, b1, &b1);
  777.  
  778.                 if (err != PARSERUTILS_OK) {
  779.                         return DOM_NO_MEM_ERR;
  780.                 }
  781.  
  782.                 i1--;
  783.         }
  784.  
  785.         /* Initialise the byte index of the end to that of the start */
  786.         b2 = b1;
  787.  
  788.         /* Calculate the byte index of the end */
  789.         while (i2 > 0) {
  790.                 err = parserutils_charset_utf8_next(t, tlen, b2, &b2);
  791.  
  792.                 if (err != PARSERUTILS_OK) {
  793.                         return DOM_NO_MEM_ERR;
  794.                 }
  795.  
  796.                 i2--;
  797.         }
  798.  
  799.         /* Allocate result string */
  800.         res = malloc(sizeof(*res));
  801.         if (res == NULL) {
  802.                 return DOM_NO_MEM_ERR;
  803.         }
  804.  
  805.         /* Allocate data buffer for result contents */
  806.         res->data.cdata.ptr = malloc(tlen + slen - (b2 - b1) + 1);
  807.         if (res->data.cdata.ptr == NULL) {
  808.                 free(res);
  809.                 return DOM_NO_MEM_ERR;
  810.         }
  811.  
  812.         /* Copy initial portion of target, if any, into result */
  813.         if (b1 > 0) {
  814.                 memcpy(res->data.cdata.ptr, t, b1);
  815.         }
  816.  
  817.         /* Copy replacement data into result */
  818.         if (slen > 0) {
  819.                 memcpy(res->data.cdata.ptr + b1, s, slen);
  820.         }
  821.  
  822.         /* Copy remainder of target, if any, into result */
  823.         if (tlen - b2 > 0) {
  824.                 memcpy(res->data.cdata.ptr + b1 + slen, t + b2, tlen - b2);
  825.         }
  826.  
  827.         res->data.cdata.ptr[tlen + slen - (b2 - b1)] = '\0';
  828.  
  829.         res->data.cdata.len = tlen + slen - (b2 - b1);
  830.  
  831.         res->base.refcnt = 1;
  832.  
  833.         res->type = DOM_STRING_CDATA;
  834.  
  835.         *result = (dom_string *)res;
  836.  
  837.         return DOM_NO_ERR;
  838. }
  839.  
  840. /**
  841.  * Calculate a hash value from a dom string
  842.  *
  843.  * \param str  The string to calculate a hash of
  844.  * \return The hash value associated with the string
  845.  */
  846. uint32_t dom_string_hash(dom_string *str)
  847. {
  848.         const uint8_t *s = (const uint8_t *) dom_string_data(str);
  849.         size_t slen = dom_string_byte_length(str);
  850.         uint32_t hash = 0x811c9dc5;
  851.  
  852.         while (slen > 0) {
  853.                 hash *= 0x01000193;
  854.                 hash ^= *s;
  855.  
  856.                 s++;
  857.                 slen--;
  858.         }
  859.  
  860.         return hash;
  861. }
  862.  
  863. /**
  864.  * Convert a lwc_error to a dom_exception
  865.  *
  866.  * \param err  The input lwc_error
  867.  * \return the dom_exception
  868.  */
  869. dom_exception _dom_exception_from_lwc_error(lwc_error err)
  870. {
  871.         switch (err) {
  872.         case lwc_error_ok:
  873.                 return DOM_NO_ERR;
  874.         case lwc_error_oom:
  875.                 return DOM_NO_MEM_ERR;
  876.         case lwc_error_range:
  877.                 return DOM_INDEX_SIZE_ERR;
  878.         }
  879.  
  880.         return DOM_NO_ERR;
  881. }
  882.  
  883. /**
  884.  * Get the raw character data of the dom_string.
  885.  *
  886.  * \param str   The dom_string object
  887.  * \return      The C string pointer
  888.  *
  889.  * @note: This function is just provided for the convenience of accessing the
  890.  * raw C string character, no change on the result string is allowed.
  891.  */
  892. const char *dom_string_data(const dom_string *str)
  893. {
  894.         dom_string_internal *istr = (dom_string_internal *) str;
  895.         if (istr->type == DOM_STRING_CDATA) {
  896.                 return (const char *) istr->data.cdata.ptr;
  897.         } else {
  898.                 return lwc_string_data(istr->data.intern);
  899.         }
  900. }
  901.  
  902. /** Get the byte length of this dom_string
  903.  *
  904.  * \param str   The dom_string object
  905.  */
  906. size_t dom_string_byte_length(const dom_string *str)
  907. {
  908.         dom_string_internal *istr = (dom_string_internal *) str;
  909.         if (istr->type == DOM_STRING_CDATA) {
  910.                 return istr->data.cdata.len;
  911.         } else {
  912.                 return lwc_string_length(istr->data.intern);
  913.         }
  914. }
  915.  
  916. /** Convert the given string to uppercase
  917.  *
  918.  * \param source
  919.  * \param ascii_only  Whether to only convert [a-z] to [A-Z]
  920.  * \param upper       Result pointer for uppercase string.  Caller owns ref
  921.  *
  922.  * \return DOM_NO_ERR on success.
  923.  *
  924.  * \note Right now, will return DOM_NOT_SUPPORTED_ERR if ascii_only is false.
  925.  */
  926. dom_exception
  927. dom_string_toupper(dom_string *source, bool ascii_only, dom_string **upper)
  928. {
  929.         const uint8_t *orig_s = (const uint8_t *) dom_string_data(source);
  930.         const size_t nbytes = dom_string_byte_length(source);
  931.         uint8_t *copy_s;
  932.         size_t index = 0, clen;
  933.         parserutils_error err;
  934.         dom_exception exc;
  935.        
  936.         if (ascii_only == false)
  937.                 return DOM_NOT_SUPPORTED_ERR;
  938.        
  939.         copy_s = malloc(nbytes);
  940.         if (copy_s == NULL)
  941.                 return DOM_NO_MEM_ERR;
  942.         memcpy(copy_s, orig_s, nbytes);
  943.        
  944.         while (index < nbytes) {
  945.                 err = parserutils_charset_utf8_char_byte_length(orig_s + index,
  946.                                                                 &clen);
  947.                 if (err != PARSERUTILS_OK) {
  948.                         free(copy_s);
  949.                         /** \todo Find a better exception */
  950.                         return DOM_NO_MEM_ERR;
  951.                 }
  952.                
  953.                 if (clen == 1) {
  954.                         if (orig_s[index] >= 'a' &&
  955.                             orig_s[index] <= 'z')
  956.                                 copy_s[index] -= 'a' - 'A';
  957.                 }
  958.                
  959.                 index += clen;
  960.         }
  961.        
  962.         if (((dom_string_internal*)source)->type == DOM_STRING_CDATA) {
  963.                 exc = dom_string_create(copy_s, nbytes, upper);
  964.         } else {
  965.                 exc = dom_string_create_interned(copy_s, nbytes, upper);
  966.         }
  967.        
  968.         free(copy_s);
  969.        
  970.         return exc;
  971. }
  972.  
  973. /** Convert the given string to lowercase
  974.  *
  975.  * \param source
  976.  * \param ascii_only  Whether to only convert [a-z] to [A-Z]
  977.  * \param lower       Result pointer for lowercase string.  Caller owns ref
  978.  *
  979.  * \return DOM_NO_ERR on success.
  980.  *
  981.  * \note Right now, will return DOM_NOT_SUPPORTED_ERR if ascii_only is false.
  982.  */
  983. dom_exception
  984. dom_string_tolower(dom_string *source, bool ascii_only, dom_string **lower)
  985. {
  986.         const uint8_t *orig_s = (const uint8_t *) dom_string_data(source);
  987.         const size_t nbytes = dom_string_byte_length(source);
  988.         uint8_t *copy_s;
  989.         size_t index = 0, clen;
  990.         parserutils_error err;
  991.         dom_exception exc;
  992.        
  993.         if (ascii_only == false)
  994.                 return DOM_NOT_SUPPORTED_ERR;
  995.        
  996.         copy_s = malloc(nbytes);
  997.         if (copy_s == NULL)
  998.                 return DOM_NO_MEM_ERR;
  999.         memcpy(copy_s, orig_s, nbytes);
  1000.        
  1001.         while (index < nbytes) {
  1002.                 err = parserutils_charset_utf8_char_byte_length(orig_s + index,
  1003.                                                                 &clen);
  1004.                 if (err != PARSERUTILS_OK) {
  1005.                         free(copy_s);
  1006.                         /** \todo Find a better exception */
  1007.                         return DOM_NO_MEM_ERR;
  1008.                 }
  1009.                
  1010.                 if (clen == 1) {
  1011.                         if (orig_s[index] >= 'A' &&
  1012.                             orig_s[index] <= 'Z')
  1013.                                 copy_s[index] += 'a' - 'A';
  1014.                 }
  1015.                
  1016.                 index += clen;
  1017.         }
  1018.        
  1019.         if (((dom_string_internal*)source)->type == DOM_STRING_CDATA) {
  1020.                 exc = dom_string_create(copy_s, nbytes, lower);
  1021.         } else {
  1022.                 exc = dom_string_create_interned(copy_s, nbytes, lower);
  1023.         }
  1024.        
  1025.         free(copy_s);
  1026.        
  1027.         return exc;
  1028. }
  1029.  
  1030.