0 votos

¿Cuál es la diferencia entre estas dos versiones de código (aritmética de punteros y unicode)?

Estoy depurando un código opensource en un sistema Solaris de 64 bits, usando GCC, que convierte caracteres de 2 bytes ( wchar_t ) a caracteres de 4 bytes ( wchar_t ). Porque Solaris como algunos otros Unixes definen wchar_t como 4byte, no 2byte como en Windows.

Ahora he arreglado el problema, mediante la disposición de la aritmética de punteros en dos líneas, pero no estoy seguro de lo que estaba mal en el código original. ¿Alguna pista?

Código original

int StringCopy2to4bytes(const unsigned short* src, int src_size, 
                         unsigned int* dst, int dst_size)
{
 int cp_size = 0;

 const unsigned short *src_end = NULL;
 const unsigned int   *dst_end = NULL;

 unsigned int c1, c2;

 src_end = src + src_size;
 dst_end = dst + dst_size;

 while (src < src_end)
 {
     c1 = *src++;
     if ((c1 >= UNI_SUR_HIGH_START) && (c1 <= UNI_SUR_HIGH_END))
     {
         if (src < src_end)
         {
             c2 = *src;
             if ((c2 >= UNI_SUR_LOW_START) && (c2 <= UNI_SUR_LOW_END))
             {
                c1 = ((c1 - UNI_SUR_HIGH_START) << UNI_SHIFT) + 
                      (c1 - UNI_SUR_LOW_START )  + UNI_BASE;

                ++src;
             }
         } 
         else 
             return -1;
     } 

     if (dst >= dst_end) return -2;

     *dst++ = c1;

     cp_size++;
 }

 return cp_size;
}

Código fijo

int StringCopy2to4bytes(const unsigned short* src, int src_size, 
                         unsigned int* dst, int dst_size)
{
 int cp_size = 0;

 const unsigned short *src_end = NULL;
 const unsigned int   *dst_end = NULL;

 unsigned int c1, c2;

 src_end = src + src_size;
 dst_end = dst + dst_size;

 while (src < src_end)
 {
     c1 = *src; //FIX
     ++src;

     if ((c1 >= UNI_SUR_HIGH_START) && (c1 <= UNI_SUR_HIGH_END))
     {
         if (src < src_end)
         {
             c2 = *src;
             if ((c2 >= UNI_SUR_LOW_START) && (c2 <= UNI_SUR_LOW_END))
             {
                c1 = ((c1 - UNI_SUR_HIGH_START) << UNI_SHIFT) + 
                      (c1 - UNI_SUR_LOW_START )  + UNI_BASE;

                ++src;
             }
         } 
         else 
             return -1;
     } 

     if (dst >= dst_end) return -2;

     *dst = c1; //FIX
     ++dst;

     cp_size++;
 }

 return cp_size;
}

Editar : para que conste, el código no es mío, sólo lo estoy usando, y resulta que lo estoy depurando, no es que haga una gran diferencia, pero el código fuente es bastante grande, así que estoy tratando de arreglarlo con pinzas como sea, no refactorizarlo todo, de todos modos los errores son errores, y necesito arreglarlo y enviar un correo al autor sobre lo que estaba mal.

Las constantes son:

/* unicode constants */
#define UNI_SHIFT             ((int) 10 )
#define UNI_BASE              ((unsigned int) 0x0010000UL)
#define UNI_MASK              ((unsigned int) 0x3FFUL)
#define UNI_REPLACEMENT_CHAR  ((unsigned int) 0x0000FFFD)
#define UNI_MAX_BMP           ((unsigned int) 0x0000FFFF)
#define UNI_MAX_UTF16         ((unsigned int) 0x0010FFFF)
#define UNI_MAX_UTF32         ((unsigned int) 0x7FFFFFFF)
#define UNI_MAX_LEGAL_UTF32   ((unsigned int) 0x0010FFFF)
#define UNI_SUR_HIGH_START    ((unsigned int) 0xD800)
#define UNI_SUR_HIGH_END      ((unsigned int) 0xDBFF)
#define UNI_SUR_LOW_START     ((unsigned int) 0xDC00)
#define UNI_SUR_LOW_END       ((unsigned int) 0xDFFF)

4voto

Jonathan Leffler Puntos 299946

El código tal y como está escrito aquí sigue teniendo errores: ¡cuando combinas c1 y c2 tienes que usar c2! Es decir, en las líneas

c1 = ((c1 - UNI_SUR_HIGH_START) << UNI_SHIFT) +
      (c1 - UNI_SUR_LOW_START ) + UNI_BASE;

La tercera aparición de c1 debería ser en realidad c2.

Además, parece tonto inicializar el puntero src_end a null y luego a src + src_size. ¿Por qué no hacerlo directamente?

Además, cp_size podría ser redundante si se conservara el inicio de la cadena; entonces sería igual a (dst - initial_dst).


Código de prueba - con corrección de c1 a c2 - usando el primer ejemplo de código, en Solaris 10, con GCC 4.3.3. Se muestran los resultados de la compilación de 32 y 64 bits. Datos de la Tabla 3.4 del Capítulo 3 del estándar Unicode (técnicamente, Unicode 5.0 en lugar de 5.1.0, pero no creo que importe).

enum { NULL = 0 };
enum { UNI_SUR_HIGH_START = 0xD800, UNI_SUR_HIGH_END = 0xDBFF,
       UNI_SUR_LOW_START  = 0xDC00, UNI_SUR_LOW_END  = 0xDFFF,
       UNI_SHIFT = 10, UNI_BASE = 0x10000 };

int StringCopy2to4bytes(const unsigned short* src, int src_size, 
                         unsigned int* dst, int dst_size)
{
 int cp_size = 0;

 const unsigned short *src_end = NULL;
 const unsigned int   *dst_end = NULL;

 unsigned int c1, c2;

 src_end = src + src_size;
 dst_end = dst + dst_size;

 while (src < src_end)
 {
     c1 = *src++;
     if ((c1 >= UNI_SUR_HIGH_START) && (c1 <= UNI_SUR_HIGH_END))
     {
         if (src < src_end)
         {
             c2 = *src;
             if ((c2 >= UNI_SUR_LOW_START) && (c2 <= UNI_SUR_LOW_END))
             {
                c1 = ((c1 - UNI_SUR_HIGH_START) << UNI_SHIFT) + 
                      (c2 - UNI_SUR_LOW_START )  + UNI_BASE;    /* Fixed */

                ++src;
             }
         } 
         else 
             return -1;
     } 

     if (dst >= dst_end) return -2;

     *dst++ = c1;

     cp_size++;
 }

 return cp_size;
}

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    unsigned short w2_chars[] = { 0x004D, 0x0430, 0x4E8C, 0xD800, 0xDF02, 0x004D };
    unsigned int   w4_wanted[] = { 0x00004D, 0x000430, 0x004E8C, 0x010302, 0x00004D };
    unsigned int   w4_actual[5];
    int w2_len = 6;
    int w4_len = 5;
    int w4_actlen;
    int i;
    int failed = 0;

    w4_actlen = StringCopy2to4bytes(w2_chars, w2_len, w4_actual, w4_len);
    if (w4_actlen != w4_len)
    {
        failed = 1;
        printf("Length mismatch: got %d, wanted %d\n", w4_actlen, w4_len);
    }
    for (i = 0; i < w4_len; i++)
    {
        if (w4_actual[i] != w4_wanted[i])
        {
            printf("Mismatch: index %d: wanted 0x%06X, actual 0x%06X\n",
                   i, w4_wanted[i], w4_actual[i]);
            failed = 1;
        }
    }
    if (failed == 0)
        printf("No problem observed\n");
    return((failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}

$ gcc -m32 -O utf.c -o utf32 && ./utf32
No problem observed
$ gcc -m64 -O utf.c -o utf64 && ./utf64
No problem observed
$

Me pregunto qué le pasa a tu compilador o a tu caso de prueba.


He aquí una versión revisada de la función StringCopy2to4bytes(). Detecta y notifica una condición de error que la original no detectaba, es decir, cuando la segunda palabra del par sustituto no es un punto de código sustituto bajo válido, devuelve el estado -3.

int StringCopy2to4bytes(const unsigned short *src, int src_size, 
                        unsigned int *dst, int dst_size)
{
    const unsigned short *src_end = src + src_size;
    const unsigned int   *dst_end = dst + dst_size;
    const unsigned int   *dst0    = dst;

    while (src < src_end)
    {
        unsigned int c1 = *src++;
        if ((c1 >= UNI_SUR_HIGH_START) && (c1 <= UNI_SUR_HIGH_END))
        {
            if (src >= src_end)
                return -1;
            unsigned int c2 = *src++;
            if ((c2 >= UNI_SUR_LOW_START) && (c2 <= UNI_SUR_LOW_END))
            {
               c1 = ((c1 - UNI_SUR_HIGH_START) << UNI_SHIFT) + 
                     (c2 - UNI_SUR_LOW_START )  + UNI_BASE;    /* Fixed */
            }
            else
                return -3;  /* Invalid second code point in surrogate pair */
        } 
        if (dst >= dst_end)
            return -2; 
        *dst++ = c1;
    }
    return dst - dst0;
}

El mismo código de prueba produce el mismo certificado de buena salud. La declaración de c2 supone que está utilizando C99 - no C89.

1voto

Johan Puntos 6127

Esto huele a que podríamos necesitar algo de () aquí.

Mira la diferencia

  1. c1 = *src++;
  2. c1 = (*src)++;
  3. c1 = *(src++);

Me gustan mucho los () ya que eliminan cierta ambigüedad sobre lo que el programador quiere hacer.

/Johan

0voto

DevSolar Puntos 18897

No debería haber diferencia, ++ se une más alto que *.

Un test.c rápido tampoco muestra ninguna diferencia:

#include <stdio.h>

int main()
{
    int a[] = { 0, 1, 2, 3, 4, 5 };
    int * p = a;
    printf( "%d\n", *p );
    printf( "%d\n", *p++ );
    printf( "%d\n", *p );
    printf( "%d\n", *(p++) );
    printf( "%d\n", *p );
    return 0;
}

Da:

0
0
1
1
2

¿Qué te hace pensar que tenías un problema que el nuevo código ha solucionado?

Editer : Encontrar un error de compilación en algo que trivial es altamente improbable. La prueba anterior se ejecutó con GCC 4.1.2.

Edita 2: C1 es unsigned int, *src es unsigned short. Sizes debería ser size_t, no int. ¿El problema con el código original persiste si corriges esos errores?

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X