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. #ifndef parserutils_charset_encodings_utf8impl_h_
  9. #define parserutils_charset_encodings_utf8impl_h_
  10.  
  11. /** \file
  12.  * UTF-8 manipulation macros (implementation).
  13.  */
  14. #include <stdint.h>
  15.  
  16. #include <stdbool.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19.  
  20. /** Number of continuation bytes for a given start byte */
  21. extern const uint8_t numContinuations[256];
  22.  
  23. /**
  24.  * Convert a UTF-8 multibyte sequence into a single UCS-4 character
  25.  *
  26.  * Encoding of UCS values outside the UTF-16 plane has been removed from
  27.  * RFC3629. This macro conforms to RFC2279, however.
  28.  *
  29.  * \param s      The sequence to process
  30.  * \param len    Length of sequence
  31.  * \param ucs4   Pointer to location to receive UCS-4 character (host endian)
  32.  * \param clen   Pointer to location to receive byte length of UTF-8 sequence
  33.  * \param error  Location to receive error code
  34.  */
  35. #define UTF8_TO_UCS4(s, len, ucs4, clen, error)                         \
  36. do {                                                                    \
  37.         uint32_t c, min;                                                \
  38.         uint8_t n;                                                      \
  39.         uint8_t i;                                                      \
  40.                                                                         \
  41.         error = PARSERUTILS_OK;                                         \
  42.                                                                         \
  43.         if (s == NULL || ucs4 == NULL || clen == NULL) {                \
  44.                 error = PARSERUTILS_BADPARM;                            \
  45.                 break;                                                  \
  46.         }                                                               \
  47.                                                                         \
  48.         if (len == 0) {                                                 \
  49.                 error = PARSERUTILS_NEEDDATA;                           \
  50.                 break;                                                  \
  51.         }                                                               \
  52.                                                                         \
  53.         c = s[0];                                                       \
  54.                                                                         \
  55.         if (c < 0x80) {                                                 \
  56.                 n = 1;                                                  \
  57.                 min = 0;                                                \
  58.         } else if ((c & 0xE0) == 0xC0) {                                \
  59.                 c &= 0x1F;                                              \
  60.                 n = 2;                                                  \
  61.                 min = 0x80;                                             \
  62.         } else if ((c & 0xF0) == 0xE0) {                                \
  63.                 c &= 0x0F;                                              \
  64.                 n = 3;                                                  \
  65.                 min = 0x800;                                            \
  66.         } else if ((c & 0xF8) == 0xF0) {                                \
  67.                 c &= 0x07;                                              \
  68.                 n = 4;                                                  \
  69.                 min = 0x10000;                                          \
  70.         } else if ((c & 0xFC) == 0xF8) {                                \
  71.                 c &= 0x03;                                              \
  72.                 n = 5;                                                  \
  73.                 min = 0x200000;                                         \
  74.         } else if ((c & 0xFE) == 0xFC) {                                \
  75.                 c &= 0x01;                                              \
  76.                 n = 6;                                                  \
  77.                 min = 0x4000000;                                        \
  78.         } else {                                                        \
  79.                 error = PARSERUTILS_INVALID;                            \
  80.                 break;                                                  \
  81.         }                                                               \
  82.                                                                         \
  83.         if (len < n) {                                                  \
  84.                 error = PARSERUTILS_NEEDDATA;                           \
  85.                 break;                                                  \
  86.         }                                                               \
  87.                                                                         \
  88.         for (i = 1; i < n; i++) {                                       \
  89.                 uint32_t t = s[i];                                      \
  90.                                                                         \
  91.                 if ((t & 0xC0) != 0x80) {                               \
  92.                         error = PARSERUTILS_INVALID;                    \
  93.                         break;                                          \
  94.                 }                                                       \
  95.                                                                         \
  96.                 c <<= 6;                                                \
  97.                 c |= t & 0x3F;                                          \
  98.         }                                                               \
  99.                                                                         \
  100.         if (error == PARSERUTILS_OK) {                                  \
  101.                 /* Detect overlong sequences, surrogates and fffe/ffff */ \
  102.                 if (c < min || (c >= 0xD800 && c <= 0xDFFF) ||          \
  103.                                 c == 0xFFFE || c == 0xFFFF) {           \
  104.                         error = PARSERUTILS_INVALID;                    \
  105.                         break;                                          \
  106.                 }                                                       \
  107.                                                                         \
  108.                 *ucs4 = c;                                              \
  109.                 *clen = n;                                              \
  110.         }                                                               \
  111. } while(0)
  112.  
  113. /**
  114.  * Convert a single UCS-4 character into a UTF-8 multibyte sequence
  115.  *
  116.  * Encoding of UCS values outside the UTF-16 plane has been removed from
  117.  * RFC3629. This macro conforms to RFC2279, however.
  118.  *
  119.  * \param ucs4   The character to process (0 <= c <= 0x7FFFFFFF) (host endian)
  120.  * \param s      Pointer to pointer to output buffer, updated on exit
  121.  * \param len    Pointer to length, in bytes, of output buffer, updated on exit
  122.  * \param error  Location to receive error code
  123.  */
  124. #define UTF8_FROM_UCS4(ucs4, s, len, error)                             \
  125. do {                                                                    \
  126.         uint8_t *buf;                                                   \
  127.         uint8_t l = 0;                                                  \
  128.                                                                         \
  129.         error = PARSERUTILS_OK;                                         \
  130.                                                                         \
  131.         if (s == NULL || *s == NULL || len == NULL) {                   \
  132.                 error = PARSERUTILS_BADPARM;                            \
  133.                 break;                                                  \
  134.         }                                                               \
  135.                                                                         \
  136.         if (ucs4 < 0x80) {                                              \
  137.                 l = 1;                                                  \
  138.         } else if (ucs4 < 0x800) {                                      \
  139.                 l = 2;                                                  \
  140.         } else if (ucs4 < 0x10000) {                                    \
  141.                 l = 3;                                                  \
  142.         } else if (ucs4 < 0x200000) {                                   \
  143.                 l = 4;                                                  \
  144.         } else if (ucs4 < 0x4000000) {                                  \
  145.                 l = 5;                                                  \
  146.         } else if (ucs4 <= 0x7FFFFFFF) {                                \
  147.                 l = 6;                                                  \
  148.         } else {                                                        \
  149.                 error = PARSERUTILS_INVALID;                            \
  150.                 break;                                                  \
  151.         }                                                               \
  152.                                                                         \
  153.         if (l > *len) {                                                 \
  154.                 error = PARSERUTILS_NOMEM;                              \
  155.                 break;                                                  \
  156.         }                                                               \
  157.                                                                         \
  158.         buf = *s;                                                       \
  159.                                                                         \
  160.         if (l == 1) {                                                   \
  161.                 buf[0] = (uint8_t) ucs4;                                \
  162.         } else {                                                        \
  163.                 uint8_t i;                                              \
  164.                 for (i = l; i > 1; i--) {                               \
  165.                         buf[i - 1] = 0x80 | (ucs4 & 0x3F);              \
  166.                         ucs4 >>= 6;                                     \
  167.                 }                                                       \
  168.                 buf[0] = ~((1 << (8 - l)) - 1) | ucs4;                  \
  169.         }                                                               \
  170.                                                                         \
  171.         *s += l;                                                        \
  172.         *len -= l;                                                      \
  173. } while(0)
  174.  
  175. /**
  176.  * Calculate the length (in characters) of a bounded UTF-8 string
  177.  *
  178.  * \param s      The string
  179.  * \param max    Maximum length
  180.  * \param len    Pointer to location to receive length of string
  181.  * \param error  Location to receive error code
  182.  */
  183. #define UTF8_LENGTH(s, max, len, error)                                 \
  184. do {                                                                    \
  185.         const uint8_t *end = s + max;                                   \
  186.         int l = 0;                                                      \
  187.                                                                         \
  188.         error = PARSERUTILS_OK;                                         \
  189.                                                                         \
  190.         if (s == NULL || len == NULL) {                                 \
  191.                 error = PARSERUTILS_BADPARM;                            \
  192.                 break;                                                  \
  193.         }                                                               \
  194.                                                                         \
  195.         while (s < end) {                                               \
  196.                 uint32_t c = s[0];                                      \
  197.                                                                         \
  198.                 if ((c & 0x80) == 0x00)                                 \
  199.                         s += 1;                                         \
  200.                 else if ((c & 0xE0) == 0xC0)                            \
  201.                         s += 2;                                         \
  202.                 else if ((c & 0xF0) == 0xE0)                            \
  203.                         s += 3;                                         \
  204.                 else if ((c & 0xF8) == 0xF0)                            \
  205.                         s += 4;                                         \
  206.                 else if ((c & 0xFC) == 0xF8)                            \
  207.                         s += 5;                                         \
  208.                 else if ((c & 0xFE) == 0xFC)                            \
  209.                         s += 6;                                         \
  210.                 else {                                                  \
  211.                         error = PARSERUTILS_INVALID;                    \
  212.                         break;                                          \
  213.                 }                                                       \
  214.                                                                         \
  215.                 l++;                                                    \
  216.         }                                                               \
  217.                                                                         \
  218.         if (error == PARSERUTILS_OK)                                    \
  219.                 *len = l;                                               \
  220. } while(0)
  221.  
  222. /**
  223.  * Calculate the length (in bytes) of a UTF-8 character
  224.  *
  225.  * \param s      Pointer to start of character
  226.  * \param len    Pointer to location to receive length
  227.  * \param error  Location to receive error code
  228.  */
  229. #define UTF8_CHAR_BYTE_LENGTH(s, len, error)                            \
  230. do {                                                                    \
  231.         if (s == NULL || len == NULL) {                                 \
  232.                 error = PARSERUTILS_BADPARM;                            \
  233.                 break;                                                  \
  234.         }                                                               \
  235.                                                                         \
  236.         *len = numContinuations[s[0]] + 1 /* Start byte */;             \
  237.                                                                         \
  238.         error = PARSERUTILS_OK;                                         \
  239. } while(0)
  240.  
  241. /**
  242.  * Find previous legal UTF-8 char in string
  243.  *
  244.  * \param s        The string
  245.  * \param off      Offset in the string to start at
  246.  * \param prevoff  Pointer to location to receive offset of first byte of
  247.  *                 previous legal character
  248.  * \param error    Location to receive error code
  249.  */
  250. #define UTF8_PREV(s, off, prevoff, error)                               \
  251. do {                                                                    \
  252.         if (s == NULL || prevoff == NULL) {                             \
  253.                 error = PARSERUTILS_BADPARM;                            \
  254.                 break;                                                  \
  255.         }                                                               \
  256.                                                                         \
  257.         while (off != 0 && (s[--off] & 0xC0) == 0x80)                   \
  258.                 /* do nothing */;                                       \
  259.                                                                         \
  260.         *prevoff = off;                                                 \
  261.                                                                         \
  262.         error = PARSERUTILS_OK;                                         \
  263. } while(0)
  264.  
  265. /**
  266.  * Find next legal UTF-8 char in string
  267.  *
  268.  * \param s        The string (assumed valid)
  269.  * \param len      Maximum offset in string
  270.  * \param off      Offset in the string to start at
  271.  * \param nextoff  Pointer to location to receive offset of first byte of
  272.  *                 next legal character
  273.  * \param error    Location to receive error code
  274.  */
  275. #define UTF8_NEXT(s, len, off, nextoff, error)                          \
  276. do {                                                                    \
  277.         if (s == NULL || off >= len || nextoff == NULL) {               \
  278.                 error = PARSERUTILS_BADPARM;                            \
  279.                 break;                                                  \
  280.         }                                                               \
  281.                                                                         \
  282.         /* Skip current start byte (if present - may be mid-sequence) */\
  283.         if (s[off] < 0x80 || (s[off] & 0xC0) == 0xC0)                   \
  284.                 off++;                                                  \
  285.                                                                         \
  286.         while (off < len && (s[off] & 0xC0) == 0x80)                    \
  287.                 off++;                                                  \
  288.                                                                         \
  289.         *nextoff = off;                                                 \
  290.                                                                         \
  291.         error = PARSERUTILS_OK;                                         \
  292. } while(0)
  293.  
  294. /**
  295.  * Skip to start of next sequence in UTF-8 input
  296.  *
  297.  * \param s        The string (assumed to be of dubious validity)
  298.  * \param len      Maximum offset in string
  299.  * \param off      Offset in the string to start at
  300.  * \param nextoff  Pointer to location to receive offset of first byte of
  301.  *                 next legal character
  302.  * \param error    Location to receive error code
  303.  */
  304. #define UTF8_NEXT_PARANOID(s, len, off, nextoff, error)                 \
  305. do {                                                                    \
  306.         uint8_t c;                                                      \
  307.                                                                         \
  308.         error = PARSERUTILS_OK;                                         \
  309.                                                                         \
  310.         if (s == NULL || off >= len || nextoff == NULL) {               \
  311.                 error = PARSERUTILS_BADPARM;                            \
  312.                 break;                                                  \
  313.         }                                                               \
  314.                                                                         \
  315.         c = s[off];                                                     \
  316.                                                                         \
  317.         /* If we're mid-sequence, simply advance to next byte */        \
  318.         if (!(c < 0x80 || (c & 0xC0) == 0xC0)) {                        \
  319.                 off++;                                                  \
  320.         } else {                                                        \
  321.                 uint32_t nCont = numContinuations[c];                   \
  322.                 uint32_t nToSkip;                                       \
  323.                                                                         \
  324.                 if (off + nCont + 1 >= len) {                           \
  325.                         error = PARSERUTILS_NEEDDATA;                   \
  326.                         break;                                          \
  327.                 }                                                       \
  328.                                                                         \
  329.                 /* Verify continuation bytes */                         \
  330.                 for (nToSkip = 1; nToSkip <= nCont; nToSkip++) {        \
  331.                         if ((s[off + nToSkip] & 0xC0) != 0x80)          \
  332.                                 break;                                  \
  333.                 }                                                       \
  334.                                                                         \
  335.                 /* Skip over the valid bytes */                         \
  336.                 off += nToSkip;                                         \
  337.         }                                                               \
  338.                                                                         \
  339.         *nextoff = off;                                                 \
  340. } while(0)
  341.  
  342. #endif
  343.