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 LibParserUtils.
  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.  */
  7.  
  8. #include <stdlib.h>
  9. #include <string.h>
  10.  
  11. #include <parserutils/charset/mibenum.h>
  12. #include <parserutils/charset/utf16.h>
  13.  
  14. #include "charset/codecs/codec_impl.h"
  15. #include "utils/endian.h"
  16. #include "utils/utils.h"
  17.  
  18. /**
  19.  * UTF-16 charset codec
  20.  */
  21. typedef struct charset_utf16_codec {
  22.         parserutils_charset_codec base; /**< Base class */
  23.  
  24. #define INVAL_BUFSIZE (32)
  25.         uint8_t inval_buf[INVAL_BUFSIZE];       /**< Buffer for fixing up
  26.                                                  * incomplete input
  27.                                                  * sequences */
  28.         size_t inval_len;               /*< Byte length of inval_buf **/
  29.  
  30. #define READ_BUFSIZE (8)
  31.         uint32_t read_buf[READ_BUFSIZE];        /**< Buffer for partial
  32.                                                  * output sequences (decode)
  33.                                                  * (host-endian) */
  34.         size_t read_len;                /**< Character length of read_buf */
  35.  
  36. #define WRITE_BUFSIZE (8)
  37.         uint32_t write_buf[WRITE_BUFSIZE];      /**< Buffer for partial
  38.                                                  * output sequences (encode)
  39.                                                  * (host-endian) */
  40.         size_t write_len;               /**< Character length of write_buf */
  41.  
  42. } charset_utf16_codec;
  43.  
  44. static bool charset_utf16_codec_handles_charset(const char *charset);
  45. static parserutils_error charset_utf16_codec_create(
  46.                 const char *charset, parserutils_alloc alloc, void *pw,
  47.                 parserutils_charset_codec **codec);
  48. static parserutils_error charset_utf16_codec_destroy(
  49.                 parserutils_charset_codec *codec);
  50. static parserutils_error charset_utf16_codec_encode(
  51.                 parserutils_charset_codec *codec,
  52.                 const uint8_t **source, size_t *sourcelen,
  53.                 uint8_t **dest, size_t *destlen);
  54. static parserutils_error charset_utf16_codec_decode(
  55.                 parserutils_charset_codec *codec,
  56.                 const uint8_t **source, size_t *sourcelen,
  57.                 uint8_t **dest, size_t *destlen);
  58. static parserutils_error charset_utf16_codec_reset(
  59.                 parserutils_charset_codec *codec);
  60. static inline parserutils_error charset_utf16_codec_read_char(
  61.                 charset_utf16_codec *c,
  62.                 const uint8_t **source, size_t *sourcelen,
  63.                 uint8_t **dest, size_t *destlen);
  64. static inline parserutils_error charset_utf16_codec_output_decoded_char(
  65.                 charset_utf16_codec *c,
  66.                 uint32_t ucs4, uint8_t **dest, size_t *destlen);
  67.  
  68. /**
  69.  * Determine whether this codec handles a specific charset
  70.  *
  71.  * \param charset  Charset to test
  72.  * \return true if handleable, false otherwise
  73.  */
  74. bool charset_utf16_codec_handles_charset(const char *charset)
  75. {
  76.         return parserutils_charset_mibenum_from_name(charset, strlen(charset))
  77.                 ==
  78.                 parserutils_charset_mibenum_from_name("UTF-16", SLEN("UTF-16"));
  79. }
  80.  
  81. /**
  82.  * Create a UTF-16 codec
  83.  *
  84.  * \param charset  The charset to read from / write to
  85.  * \param alloc    Memory (de)allocation function
  86.  * \param pw       Pointer to client-specific private data (may be NULL)
  87.  * \param codec    Pointer to location to receive codec
  88.  * \return PARSERUTILS_OK on success,
  89.  *         PARSERUTILS_BADPARM on bad parameters,
  90.  *         PARSERUTILS_NOMEM on memory exhausion
  91.  */
  92. parserutils_error charset_utf16_codec_create(const char *charset,
  93.                 parserutils_alloc alloc, void *pw,
  94.                 parserutils_charset_codec **codec)
  95. {
  96.         charset_utf16_codec *c;
  97.  
  98.         UNUSED(charset);
  99.  
  100.         c = alloc(NULL, sizeof(charset_utf16_codec), pw);
  101.         if (c == NULL)
  102.                 return PARSERUTILS_NOMEM;
  103.  
  104.         c->inval_buf[0] = '\0';
  105.         c->inval_len = 0;
  106.  
  107.         c->read_buf[0] = 0;
  108.         c->read_len = 0;
  109.  
  110.         c->write_buf[0] = 0;
  111.         c->write_len = 0;
  112.  
  113.         /* Finally, populate vtable */
  114.         c->base.handler.destroy = charset_utf16_codec_destroy;
  115.         c->base.handler.encode = charset_utf16_codec_encode;
  116.         c->base.handler.decode = charset_utf16_codec_decode;
  117.         c->base.handler.reset = charset_utf16_codec_reset;
  118.  
  119.         *codec = (parserutils_charset_codec *) c;
  120.  
  121.         return PARSERUTILS_OK;
  122. }
  123.  
  124. /**
  125.  * Destroy a UTF-16 codec
  126.  *
  127.  * \param codec  The codec to destroy
  128.  * \return PARSERUTILS_OK on success, appropriate error otherwise
  129.  */
  130. parserutils_error charset_utf16_codec_destroy (parserutils_charset_codec *codec)
  131. {
  132.         UNUSED(codec);
  133.  
  134.         return PARSERUTILS_OK;
  135. }
  136.  
  137. /**
  138.  * Encode a chunk of UCS-4 (big endian) data into UTF-16
  139.  *
  140.  * \param codec      The codec to use
  141.  * \param source     Pointer to pointer to source data
  142.  * \param sourcelen  Pointer to length (in bytes) of source data
  143.  * \param dest       Pointer to pointer to output buffer
  144.  * \param destlen    Pointer to length (in bytes) of output buffer
  145.  * \return PARSERUTILS_OK          on success,
  146.  *         PARSERUTILS_NOMEM       if output buffer is too small,
  147.  *         PARSERUTILS_INVALID     if a character cannot be represented and the
  148.  *                            codec's error handling mode is set to STRICT,
  149.  *
  150.  * On exit, ::source will point immediately _after_ the last input character
  151.  * read. Any remaining output for the character will be buffered by the
  152.  * codec for writing on the next call.
  153.  *
  154.  * Note that, if failure occurs whilst attempting to write any output
  155.  * buffered by the last call, then ::source and ::sourcelen will remain
  156.  * unchanged (as nothing more has been read).
  157.  *
  158.  * ::sourcelen will be reduced appropriately on exit.
  159.  *
  160.  * ::dest will point immediately _after_ the last character written.
  161.  *
  162.  * ::destlen will be reduced appropriately on exit.
  163.  */
  164. parserutils_error charset_utf16_codec_encode(parserutils_charset_codec *codec,
  165.                 const uint8_t **source, size_t *sourcelen,
  166.                 uint8_t **dest, size_t *destlen)
  167. {
  168.         charset_utf16_codec *c = (charset_utf16_codec *) codec;
  169.         uint32_t ucs4;
  170.         uint32_t *towrite;
  171.         size_t towritelen;
  172.         parserutils_error error;
  173.  
  174.         /* Process any outstanding characters from the previous call */
  175.         if (c->write_len > 0) {
  176.                 uint32_t *pwrite = c->write_buf;
  177.                 uint8_t buf[4];
  178.                 size_t len;
  179.  
  180.                 while (c->write_len > 0) {
  181.                         error = parserutils_charset_utf16_from_ucs4(
  182.                                         pwrite[0], buf, &len);
  183.                         if (error != PARSERUTILS_OK)
  184.                                 abort();
  185.  
  186.                         if (*destlen < len) {
  187.                                 /* Insufficient output buffer space */
  188.                                 for (len = 0; len < c->write_len; len++)
  189.                                         c->write_buf[len] = pwrite[len];
  190.  
  191.                                 return PARSERUTILS_NOMEM;
  192.                         }
  193.  
  194.                         memcpy(*dest, buf, len);
  195.  
  196.                         *dest += len;
  197.                         *destlen -= len;
  198.  
  199.                         pwrite++;
  200.                         c->write_len--;
  201.                 }
  202.         }
  203.  
  204.         /* Now process the characters for this call */
  205.         while (*sourcelen > 0) {
  206.                 ucs4 = endian_big_to_host(*((uint32_t *) (void *) *source));
  207.                 towrite = &ucs4;
  208.                 towritelen = 1;
  209.  
  210.                 /* Output current characters */
  211.                 while (towritelen > 0) {
  212.                         uint8_t buf[4];
  213.                         size_t len;
  214.  
  215.                         error = parserutils_charset_utf16_from_ucs4(
  216.                                         towrite[0], buf, &len);
  217.                         if (error != PARSERUTILS_OK)
  218.                                 abort();
  219.  
  220.                         if (*destlen < len) {
  221.                                 /* Insufficient output space */
  222.                                 if (towritelen >= WRITE_BUFSIZE)
  223.                                         abort();
  224.  
  225.                                 c->write_len = towritelen;
  226.  
  227.                                 /* Copy pending chars to save area, for
  228.                                  * processing next call. */
  229.                                 for (len = 0; len < towritelen; len++)
  230.                                         c->write_buf[len] = towrite[len];
  231.  
  232.                                 /* Claim character we've just buffered,
  233.                                  * so it's not reprocessed */
  234.                                 *source += 4;
  235.                                 *sourcelen -= 4;
  236.  
  237.                                 return PARSERUTILS_NOMEM;
  238.                         }
  239.  
  240.                         memcpy(*dest, buf, len);
  241.  
  242.                         *dest += len;
  243.                         *destlen -= len;
  244.  
  245.                         towrite++;
  246.                         towritelen--;
  247.                 }
  248.  
  249.                 *source += 4;
  250.                 *sourcelen -= 4;
  251.         }
  252.  
  253.         return PARSERUTILS_OK;
  254. }
  255.  
  256. /**
  257.  * Decode a chunk of UTF-16 data into UCS-4 (big endian)
  258.  *
  259.  * \param codec      The codec to use
  260.  * \param source     Pointer to pointer to source data
  261.  * \param sourcelen  Pointer to length (in bytes) of source data
  262.  * \param dest       Pointer to pointer to output buffer
  263.  * \param destlen    Pointer to length (in bytes) of output buffer
  264.  * \return PARSERUTILS_OK          on success,
  265.  *         PARSERUTILS_NOMEM       if output buffer is too small,
  266.  *         PARSERUTILS_INVALID     if a character cannot be represented and the
  267.  *                            codec's error handling mode is set to STRICT,
  268.  *
  269.  * On exit, ::source will point immediately _after_ the last input character
  270.  * read, if the result is _OK or _NOMEM. Any remaining output for the
  271.  * character will be buffered by the codec for writing on the next call.
  272.  *
  273.  * In the case of the result being _INVALID, ::source will point _at_ the
  274.  * last input character read; nothing will be written or buffered for the
  275.  * failed character. It is up to the client to fix the cause of the failure
  276.  * and retry the decoding process.
  277.  *
  278.  * Note that, if failure occurs whilst attempting to write any output
  279.  * buffered by the last call, then ::source and ::sourcelen will remain
  280.  * unchanged (as nothing more has been read).
  281.  *
  282.  * If STRICT error handling is configured and an illegal sequence is split
  283.  * over two calls, then _INVALID will be returned from the second call,
  284.  * but ::source will point mid-way through the invalid sequence (i.e. it
  285.  * will be unmodified over the second call). In addition, the internal
  286.  * incomplete-sequence buffer will be emptied, such that subsequent calls
  287.  * will progress, rather than re-evaluating the same invalid sequence.
  288.  *
  289.  * ::sourcelen will be reduced appropriately on exit.
  290.  *
  291.  * ::dest will point immediately _after_ the last character written.
  292.  *
  293.  * ::destlen will be reduced appropriately on exit.
  294.  *
  295.  * Call this with a source length of 0 to flush the output buffer.
  296.  */
  297. parserutils_error charset_utf16_codec_decode(parserutils_charset_codec *codec,
  298.                 const uint8_t **source, size_t *sourcelen,
  299.                 uint8_t **dest, size_t *destlen)
  300. {
  301.         charset_utf16_codec *c = (charset_utf16_codec *) codec;
  302.         parserutils_error error;
  303.  
  304.         if (c->read_len > 0) {
  305.                 /* Output left over from last decode */
  306.                 uint32_t *pread = c->read_buf;
  307.  
  308.                 while (c->read_len > 0 && *destlen >= c->read_len * 4) {
  309.                         *((uint32_t *) (void *) *dest) =
  310.                                         endian_host_to_big(pread[0]);
  311.  
  312.                         *dest += 4;
  313.                         *destlen -= 4;
  314.  
  315.                         pread++;
  316.                         c->read_len--;
  317.                 }
  318.  
  319.                 if (*destlen < c->read_len * 4) {
  320.                         /* Ran out of output buffer */
  321.                         size_t i;
  322.  
  323.                         /* Shuffle remaining output down */
  324.                         for (i = 0; i < c->read_len; i++)
  325.                                 c->read_buf[i] = pread[i];
  326.  
  327.                         return PARSERUTILS_NOMEM;
  328.                 }
  329.         }
  330.  
  331.         if (c->inval_len > 0) {
  332.                 /* The last decode ended in an incomplete sequence.
  333.                  * Fill up inval_buf with data from the start of the
  334.                  * new chunk and process it. */
  335.                 uint8_t *in = c->inval_buf;
  336.                 size_t ol = c->inval_len;
  337.                 size_t l = min(INVAL_BUFSIZE - ol - 1, *sourcelen);
  338.                 size_t orig_l = l;
  339.  
  340.                 memcpy(c->inval_buf + ol, *source, l);
  341.  
  342.                 l += c->inval_len;
  343.  
  344.                 error = charset_utf16_codec_read_char(c,
  345.                                 (const uint8_t **) &in, &l, dest, destlen);
  346.                 if (error != PARSERUTILS_OK && error != PARSERUTILS_NOMEM) {
  347.                         return error;
  348.                 }
  349.  
  350.                 /* And now, fix up source pointers */
  351.                 *source += max((signed) (orig_l - l), 0);
  352.                 *sourcelen -= max((signed) (orig_l - l), 0);
  353.  
  354.                 /* Failed to resolve an incomplete character and
  355.                  * ran out of buffer space. No recovery strategy
  356.                  * possible, so explode everywhere. */
  357.                 if ((orig_l + ol) - l == 0)
  358.                         abort();
  359.  
  360.                 /* Report memory exhaustion case from above */
  361.                 if (error != PARSERUTILS_OK)
  362.                         return error;
  363.         }
  364.  
  365.         /* Finally, the "normal" case; process all outstanding characters */
  366.         while (*sourcelen > 0) {
  367.                 error = charset_utf16_codec_read_char(c,
  368.                                 source, sourcelen, dest, destlen);
  369.                 if (error != PARSERUTILS_OK) {
  370.                         return error;
  371.                 }
  372.         }
  373.  
  374.         return PARSERUTILS_OK;
  375. }
  376.  
  377. /**
  378.  * Clear a UTF-16 codec's encoding state
  379.  *
  380.  * \param codec  The codec to reset
  381.  * \return PARSERUTILS_OK on success, appropriate error otherwise
  382.  */
  383. parserutils_error charset_utf16_codec_reset(parserutils_charset_codec *codec)
  384. {
  385.         charset_utf16_codec *c = (charset_utf16_codec *) codec;
  386.  
  387.         c->inval_buf[0] = '\0';
  388.         c->inval_len = 0;
  389.  
  390.         c->read_buf[0] = 0;
  391.         c->read_len = 0;
  392.  
  393.         c->write_buf[0] = 0;
  394.         c->write_len = 0;
  395.  
  396.         return PARSERUTILS_OK;
  397. }
  398.  
  399.  
  400. /**
  401.  * Read a character from the UTF-16 to UCS-4 (big endian)
  402.  *
  403.  * \param c          The codec
  404.  * \param source     Pointer to pointer to source buffer (updated on exit)
  405.  * \param sourcelen  Pointer to length of source buffer (updated on exit)
  406.  * \param dest       Pointer to pointer to output buffer (updated on exit)
  407.  * \param destlen    Pointer to length of output buffer (updated on exit)
  408.  * \return PARSERUTILS_OK on success,
  409.  *         PARSERUTILS_NOMEM       if output buffer is too small,
  410.  *         PARSERUTILS_INVALID     if a character cannot be represented and the
  411.  *                            codec's error handling mode is set to STRICT,
  412.  *
  413.  * On exit, ::source will point immediately _after_ the last input character
  414.  * read, if the result is _OK or _NOMEM. Any remaining output for the
  415.  * character will be buffered by the codec for writing on the next call.
  416.  *
  417.  * In the case of the result being _INVALID, ::source will point _at_ the
  418.  * last input character read; nothing will be written or buffered for the
  419.  * failed character. It is up to the client to fix the cause of the failure
  420.  * and retry the decoding process.
  421.  *
  422.  * ::sourcelen will be reduced appropriately on exit.
  423.  *
  424.  * ::dest will point immediately _after_ the last character written.
  425.  *
  426.  * ::destlen will be reduced appropriately on exit.
  427.  */
  428. parserutils_error charset_utf16_codec_read_char(charset_utf16_codec *c,
  429.                 const uint8_t **source, size_t *sourcelen,
  430.                 uint8_t **dest, size_t *destlen)
  431. {
  432.         uint32_t ucs4;
  433.         size_t sucs4;
  434.         parserutils_error error;
  435.  
  436.         /* Convert a single character */
  437.         error = parserutils_charset_utf16_to_ucs4(*source, *sourcelen,
  438.                         &ucs4, &sucs4);
  439.         if (error == PARSERUTILS_OK) {
  440.                 /* Read a character */
  441.                 error = charset_utf16_codec_output_decoded_char(c,
  442.                                 ucs4, dest, destlen);
  443.                 if (error == PARSERUTILS_OK || error == PARSERUTILS_NOMEM) {
  444.                         /* output succeeded; update source pointers */
  445.                         *source += sucs4;
  446.                         *sourcelen -= sucs4;
  447.                 }
  448.  
  449.                 /* Clear inval buffer */
  450.                 c->inval_buf[0] = '\0';
  451.                 c->inval_len = 0;
  452.  
  453.                 return error;
  454.         } else if (error == PARSERUTILS_NEEDDATA) {
  455.                 /* Incomplete input sequence */
  456.                 if (*sourcelen > INVAL_BUFSIZE)
  457.                         abort();
  458.  
  459.                 memmove(c->inval_buf, *source, *sourcelen);
  460.                 c->inval_buf[*sourcelen] = '\0';
  461.                 c->inval_len = *sourcelen;
  462.  
  463.                 *source += *sourcelen;
  464.                 *sourcelen = 0;
  465.  
  466.                 return PARSERUTILS_OK;
  467.         } else if (error == PARSERUTILS_INVALID) {
  468.                 /* Illegal input sequence */
  469.                 uint32_t nextchar;
  470.  
  471.                 /* Clear inval buffer */
  472.                 c->inval_buf[0] = '\0';
  473.                 c->inval_len = 0;
  474.  
  475.                 /* Strict errormode; simply flag invalid character */
  476.                 if (c->base.errormode ==
  477.                                 PARSERUTILS_CHARSET_CODEC_ERROR_STRICT) {
  478.                         return PARSERUTILS_INVALID;
  479.                 }
  480.  
  481.                 /* Find next valid UTF-16 sequence.
  482.                  * We're processing client-provided data, so let's
  483.                  * be paranoid about its validity. */
  484.                 error = parserutils_charset_utf16_next_paranoid(
  485.                                 *source, *sourcelen, 0, &nextchar);
  486.                 if (error != PARSERUTILS_OK) {
  487.                         if (error == PARSERUTILS_NEEDDATA) {
  488.                                 /* Need more data to be sure */
  489.                                 if (*sourcelen > INVAL_BUFSIZE)
  490.                                         abort();
  491.  
  492.                                 memmove(c->inval_buf, *source, *sourcelen);
  493.                                 c->inval_buf[*sourcelen] = '\0';
  494.                                 c->inval_len = *sourcelen;
  495.  
  496.                                 *source += *sourcelen;
  497.                                 *sourcelen = 0;
  498.  
  499.                                 nextchar = 0;
  500.                         } else {
  501.                                 return error;
  502.                         }
  503.                 }
  504.  
  505.                 /* output U+FFFD and continue processing. */
  506.                 error = charset_utf16_codec_output_decoded_char(c,
  507.                                 0xFFFD, dest, destlen);
  508.                 if (error == PARSERUTILS_OK || error == PARSERUTILS_NOMEM) {
  509.                         /* output succeeded; update source pointers */
  510.                         *source += nextchar;
  511.                         *sourcelen -= nextchar;
  512.                 }
  513.  
  514.                 return error;
  515.         }
  516.  
  517.         return PARSERUTILS_OK;
  518. }
  519.  
  520. /**
  521.  * Output a UCS-4 character (big endian)
  522.  *
  523.  * \param c        Codec to use
  524.  * \param ucs4     UCS-4 character (host endian)
  525.  * \param dest     Pointer to pointer to output buffer
  526.  * \param destlen  Pointer to output buffer length
  527.  * \return PARSERUTILS_OK          on success,
  528.  *         PARSERUTILS_NOMEM       if output buffer is too small,
  529.  */
  530. parserutils_error charset_utf16_codec_output_decoded_char(charset_utf16_codec *c,
  531.                 uint32_t ucs4, uint8_t **dest, size_t *destlen)
  532. {
  533.         if (*destlen < 4) {
  534.                 /* Run out of output buffer */
  535.                 c->read_len = 1;
  536.                 c->read_buf[0] = ucs4;
  537.  
  538.                 return PARSERUTILS_NOMEM;
  539.         }
  540.  
  541.         *((uint32_t *) (void *) *dest) = endian_host_to_big(ucs4);
  542.         *dest += 4;
  543.         *destlen -= 4;
  544.  
  545.         return PARSERUTILS_OK;
  546. }
  547.  
  548.  
  549. const parserutils_charset_handler charset_utf16_codec_handler = {
  550.         charset_utf16_codec_handles_charset,
  551.         charset_utf16_codec_create
  552. };
  553.