gif: bacport fix for bug 4451: SDL-1.2
authorOzkan Sezer <sezeroz@gmail.com>
Wed, 10 Jul 2019 23:44:50 +0300
branchSDL-1.2
changeset 678776aa1fbe453
parent 677 b875cfe43ddf
child 679 8d6699dd9d74
gif: bacport fix for bug 4451:
Loading GIFs on multiple threads returns garbage output and crashes
mainstream commits:
https://hg.libsdl.org/SDL_image/rev/07fe7a23da89
https://hg.libsdl.org/SDL_image/rev/02e1f7b5746c
IMG_gif.c
     1.1 --- a/IMG_gif.c	Wed Jul 10 23:40:40 2019 +0300
     1.2 +++ b/IMG_gif.c	Wed Jul 10 23:44:50 2019 +0300
     1.3 @@ -121,33 +121,48 @@
     1.4  
     1.5  #define LM_to_uint(a,b)			(((b)<<8)|(a))
     1.6  
     1.7 -static struct {
     1.8 -    unsigned int Width;
     1.9 -    unsigned int Height;
    1.10 -    unsigned char ColorMap[3][MAXCOLORMAPSIZE];
    1.11 -    unsigned int BitPixel;
    1.12 -    unsigned int ColorResolution;
    1.13 -    unsigned int Background;
    1.14 -    unsigned int AspectRatio;
    1.15 -    int GrayScale;
    1.16 -} GifScreen;
    1.17 +typedef struct {
    1.18 +    struct {
    1.19 +        unsigned int Width;
    1.20 +        unsigned int Height;
    1.21 +        unsigned char ColorMap[3][MAXCOLORMAPSIZE];
    1.22 +        unsigned int BitPixel;
    1.23 +        unsigned int ColorResolution;
    1.24 +        unsigned int Background;
    1.25 +        unsigned int AspectRatio;
    1.26 +        int GrayScale;
    1.27 +    } GifScreen;
    1.28  
    1.29 -static struct {
    1.30 -    int transparent;
    1.31 -    int delayTime;
    1.32 -    int inputFlag;
    1.33 -    int disposal;
    1.34 -} Gif89;
    1.35 +    struct {
    1.36 +        int transparent;
    1.37 +        int delayTime;
    1.38 +        int inputFlag;
    1.39 +        int disposal;
    1.40 +    } Gif89;
    1.41 +
    1.42 +    unsigned char buf[280];
    1.43 +    int curbit, lastbit, done, last_byte;
    1.44 +
    1.45 +    int fresh;
    1.46 +    int code_size, set_code_size;
    1.47 +    int max_code, max_code_size;
    1.48 +    int firstcode, oldcode;
    1.49 +    int clear_code, end_code;
    1.50 +    int table[2][(1 << MAX_LWZ_BITS)];
    1.51 +    int stack[(1 << (MAX_LWZ_BITS)) * 2], *sp;
    1.52 +
    1.53 +    int ZeroDataBlock;
    1.54 +} State_t;
    1.55  
    1.56  static int ReadColorMap(SDL_RWops * src, int number,
    1.57  			unsigned char buffer[3][MAXCOLORMAPSIZE], int *flag);
    1.58 -static int DoExtension(SDL_RWops * src, int label);
    1.59 -static int GetDataBlock(SDL_RWops * src, unsigned char *buf);
    1.60 -static int GetCode(SDL_RWops * src, int code_size, int flag);
    1.61 -static int LWZReadByte(SDL_RWops * src, int flag, int input_code_size);
    1.62 +static int DoExtension(SDL_RWops * src, int label, State_t * state);
    1.63 +static int GetDataBlock(SDL_RWops * src, unsigned char *buf, State_t * state);
    1.64 +static int GetCode(SDL_RWops * src, int code_size, int flag, State_t * state);
    1.65 +static int LWZReadByte(SDL_RWops * src, int flag, int input_code_size, State_t * state);
    1.66  static Image *ReadImage(SDL_RWops * src, int len, int height, int,
    1.67  			unsigned char cmap[3][MAXCOLORMAPSIZE],
    1.68 -			int gray, int interlace, int ignore);
    1.69 +			int gray, int interlace, int ignore, State_t * state);
    1.70  
    1.71  Image *
    1.72  IMG_LoadGIF_RW(SDL_RWops *src)
    1.73 @@ -163,6 +178,10 @@
    1.74      char version[4];
    1.75      int imageNumber = 1;
    1.76      Image *image = NULL;
    1.77 +    State_t state;
    1.78 +    state.ZeroDataBlock = FALSE;
    1.79 +    state.fresh = FALSE;
    1.80 +    state.last_byte = 0;
    1.81  
    1.82      if ( src == NULL ) {
    1.83  	return NULL;
    1.84 @@ -184,25 +203,25 @@
    1.85  	RWSetMsg("bad version number, not '87a' or '89a'");
    1.86          goto done;
    1.87      }
    1.88 -    Gif89.transparent = -1;
    1.89 -    Gif89.delayTime = -1;
    1.90 -    Gif89.inputFlag = -1;
    1.91 -    Gif89.disposal = 0;
    1.92 +    state.Gif89.transparent = -1;
    1.93 +    state.Gif89.delayTime = -1;
    1.94 +    state.Gif89.inputFlag = -1;
    1.95 +    state.Gif89.disposal = 0;
    1.96  
    1.97      if (!ReadOK(src, buf, 7)) {
    1.98  	RWSetMsg("failed to read screen descriptor");
    1.99          goto done;
   1.100      }
   1.101 -    GifScreen.Width = LM_to_uint(buf[0], buf[1]);
   1.102 -    GifScreen.Height = LM_to_uint(buf[2], buf[3]);
   1.103 -    GifScreen.BitPixel = 2 << (buf[4] & 0x07);
   1.104 -    GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
   1.105 -    GifScreen.Background = buf[5];
   1.106 -    GifScreen.AspectRatio = buf[6];
   1.107 +    state.GifScreen.Width = LM_to_uint(buf[0], buf[1]);
   1.108 +    state.GifScreen.Height = LM_to_uint(buf[2], buf[3]);
   1.109 +    state.GifScreen.BitPixel = 2 << (buf[4] & 0x07);
   1.110 +    state.GifScreen.ColorResolution = (((buf[4] & 0x70) >> 3) + 1);
   1.111 +    state.GifScreen.Background = buf[5];
   1.112 +    state.GifScreen.AspectRatio = buf[6];
   1.113  
   1.114      if (BitSet(buf[4], LOCALCOLORMAP)) {	/* Global Colormap */
   1.115 -	if (ReadColorMap(src, GifScreen.BitPixel, GifScreen.ColorMap,
   1.116 -			 &GifScreen.GrayScale)) {
   1.117 +	if (ReadColorMap(src, state.GifScreen.BitPixel,
   1.118 +			 state.GifScreen.ColorMap, &state.GifScreen.GrayScale)) {
   1.119  	    RWSetMsg("error reading global colormap");
   1.120              goto done;
   1.121  	}
   1.122 @@ -224,7 +243,7 @@
   1.123  		RWSetMsg("EOF / read error on extention function code");
   1.124                  goto done;
   1.125  	    }
   1.126 -	    DoExtension(src, c);
   1.127 +	    DoExtension(src, c, &state);
   1.128  	    continue;
   1.129  	}
   1.130  	if (c != ',') {		/* Not a valid start character */
   1.131 @@ -249,19 +268,19 @@
   1.132  			      LM_to_uint(buf[6], buf[7]),
   1.133  			      bitPixel, localColorMap, grayScale,
   1.134  			      BitSet(buf[8], INTERLACE),
   1.135 -			      imageCount != imageNumber);
   1.136 +			      imageCount != imageNumber, &state);
   1.137  	} else {
   1.138  	    image = ReadImage(src, LM_to_uint(buf[4], buf[5]),
   1.139  			      LM_to_uint(buf[6], buf[7]),
   1.140 -			      GifScreen.BitPixel, GifScreen.ColorMap,
   1.141 -			      GifScreen.GrayScale, BitSet(buf[8], INTERLACE),
   1.142 -			      imageCount != imageNumber);
   1.143 +			      state.GifScreen.BitPixel, state.GifScreen.ColorMap,
   1.144 +			      state.GifScreen.GrayScale, BitSet(buf[8], INTERLACE),
   1.145 +			      imageCount != imageNumber, &state);
   1.146  	}
   1.147      } while (image == NULL);
   1.148  
   1.149  #ifdef USED_BY_SDL
   1.150 -    if ( Gif89.transparent >= 0 ) {
   1.151 -        SDL_SetColorKey(image, SDL_SRCCOLORKEY, Gif89.transparent);
   1.152 +    if ( state.Gif89.transparent >= 0 ) {
   1.153 +        SDL_SetColorKey(image, SDL_SRCCOLORKEY, state.Gif89.transparent);
   1.154      }
   1.155  #endif
   1.156  
   1.157 @@ -306,9 +325,9 @@
   1.158  }
   1.159  
   1.160  static int
   1.161 -DoExtension(SDL_RWops *src, int label)
   1.162 +DoExtension(SDL_RWops *src, int label, State_t * state)
   1.163  {
   1.164 -    static unsigned char buf[256];
   1.165 +    unsigned char buf[256];
   1.166      char *str;
   1.167  
   1.168      switch (label) {
   1.169 @@ -320,19 +339,19 @@
   1.170  	break;
   1.171      case 0xfe:			/* Comment Extension */
   1.172  	str = "Comment Extension";
   1.173 -	while (GetDataBlock(src, (unsigned char *) buf) > 0)
   1.174 +	while (GetDataBlock(src, (unsigned char *) buf, state) > 0)
   1.175  	    ;
   1.176  	return FALSE;
   1.177      case 0xf9:			/* Graphic Control Extension */
   1.178  	str = "Graphic Control Extension";
   1.179 -	(void) GetDataBlock(src, (unsigned char *) buf);
   1.180 -	Gif89.disposal = (buf[0] >> 2) & 0x7;
   1.181 -	Gif89.inputFlag = (buf[0] >> 1) & 0x1;
   1.182 -	Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
   1.183 +	(void) GetDataBlock(src, (unsigned char *) buf, state);
   1.184 +	state->Gif89.disposal = (buf[0] >> 2) & 0x7;
   1.185 +	state->Gif89.inputFlag = (buf[0] >> 1) & 0x1;
   1.186 +	state->Gif89.delayTime = LM_to_uint(buf[1], buf[2]);
   1.187  	if ((buf[0] & 0x1) != 0)
   1.188 -	    Gif89.transparent = buf[3];
   1.189 +	    state->Gif89.transparent = buf[3];
   1.190  
   1.191 -	while (GetDataBlock(src, (unsigned char *) buf) > 0)
   1.192 +	while (GetDataBlock(src, (unsigned char *) buf, state) > 0)
   1.193  	    ;
   1.194  	return FALSE;
   1.195      default:
   1.196 @@ -341,16 +360,14 @@
   1.197  	break;
   1.198      }
   1.199  
   1.200 -    while (GetDataBlock(src, (unsigned char *) buf) > 0)
   1.201 +    while (GetDataBlock(src, (unsigned char *) buf, state) > 0)
   1.202  	;
   1.203  
   1.204      return FALSE;
   1.205  }
   1.206  
   1.207 -static int ZeroDataBlock = FALSE;
   1.208 -
   1.209  static int
   1.210 -GetDataBlock(SDL_RWops *src, unsigned char *buf)
   1.211 +GetDataBlock(SDL_RWops *src, unsigned char *buf, State_t * state)
   1.212  {
   1.213      unsigned char count;
   1.214  
   1.215 @@ -358,7 +375,7 @@
   1.216  	/* pm_message("error in getting DataBlock size" ); */
   1.217  	return -1;
   1.218      }
   1.219 -    ZeroDataBlock = count == 0;
   1.220 +    state->ZeroDataBlock = count == 0;
   1.221  
   1.222      if ((count != 0) && (!ReadOK(src, buf, count))) {
   1.223  	/* pm_message("error in reading DataBlock" ); */
   1.224 @@ -368,55 +385,46 @@
   1.225  }
   1.226  
   1.227  static int
   1.228 -GetCode(SDL_RWops *src, int code_size, int flag)
   1.229 +GetCode(SDL_RWops *src, int code_size, int flag, State_t * state)
   1.230  {
   1.231 -    static unsigned char buf[280];
   1.232 -    static int curbit, lastbit, done, last_byte;
   1.233      int i, j, ret;
   1.234      unsigned char count;
   1.235  
   1.236      if (flag) {
   1.237 -	curbit = 0;
   1.238 -	lastbit = 0;
   1.239 -	done = FALSE;
   1.240 +	state->curbit = 0;
   1.241 +	state->lastbit = 0;
   1.242 +	state->done = FALSE;
   1.243  	return 0;
   1.244      }
   1.245 -    if ((curbit + code_size) >= lastbit) {
   1.246 -	if (done) {
   1.247 -	    if (curbit >= lastbit)
   1.248 +    if ((state->curbit + code_size) >= state->lastbit) {
   1.249 +	if (state->done) {
   1.250 +	    if (state->curbit >= state->lastbit)
   1.251  		RWSetMsg("ran off the end of my bits");
   1.252  	    return -1;
   1.253  	}
   1.254 -	buf[0] = buf[last_byte - 2];
   1.255 -	buf[1] = buf[last_byte - 1];
   1.256 +	state->buf[0] = state->buf[state->last_byte - 2];
   1.257 +	state->buf[1] = state->buf[state->last_byte - 1];
   1.258  
   1.259 -	if ((count = GetDataBlock(src, &buf[2])) <= 0)
   1.260 -	    done = TRUE;
   1.261 +	if ((count = GetDataBlock(src, &state->buf[2], state)) <= 0)
   1.262 +	    state->done = TRUE;
   1.263  
   1.264 -	last_byte = 2 + count;
   1.265 -	curbit = (curbit - lastbit) + 16;
   1.266 -	lastbit = (2 + count) * 8;
   1.267 +	state->last_byte = 2 + count;
   1.268 +	state->curbit = (state->curbit - state->lastbit) + 16;
   1.269 +	state->lastbit = (2 + count) * 8;
   1.270      }
   1.271      ret = 0;
   1.272 -    for (i = curbit, j = 0; j < code_size; ++i, ++j)
   1.273 -	ret |= ((buf[i / 8] & (1 << (i % 8))) != 0) << j;
   1.274 +    for (i = state->curbit, j = 0; j < code_size; ++i, ++j)
   1.275 +	ret |= ((state->buf[i / 8] & (1 << (i % 8))) != 0) << j;
   1.276  
   1.277 -    curbit += code_size;
   1.278 +    state->curbit += code_size;
   1.279  
   1.280      return ret;
   1.281  }
   1.282  
   1.283  static int
   1.284 -LWZReadByte(SDL_RWops *src, int flag, int input_code_size)
   1.285 +LWZReadByte(SDL_RWops *src, int flag, int input_code_size, State_t * state)
   1.286  {
   1.287 -    static int fresh = FALSE;
   1.288      int code, incode;
   1.289 -    static int code_size, set_code_size;
   1.290 -    static int max_code, max_code_size;
   1.291 -    static int firstcode, oldcode;
   1.292 -    static int clear_code, end_code;
   1.293 -    static int table[2][(1 << MAX_LWZ_BITS)];
   1.294 -    static int stack[(1 << (MAX_LWZ_BITS)) * 2], *sp;
   1.295      register int i;
   1.296  
   1.297      /* Fixed buffer overflow found by Michael Skladnikiewicz */
   1.298 @@ -424,60 +432,60 @@
   1.299          return -1;
   1.300  
   1.301      if (flag) {
   1.302 -	set_code_size = input_code_size;
   1.303 -	code_size = set_code_size + 1;
   1.304 -	clear_code = 1 << set_code_size;
   1.305 -	end_code = clear_code + 1;
   1.306 -	max_code_size = 2 * clear_code;
   1.307 -	max_code = clear_code + 2;
   1.308 +	state->set_code_size = input_code_size;
   1.309 +	state->code_size = state->set_code_size + 1;
   1.310 +	state->clear_code = 1 << state->set_code_size;
   1.311 +	state->end_code = state->clear_code + 1;
   1.312 +	state->max_code_size = 2 * state->clear_code;
   1.313 +	state->max_code = state->clear_code + 2;
   1.314  
   1.315 -	GetCode(src, 0, TRUE);
   1.316 +	GetCode(src, 0, TRUE, state);
   1.317  
   1.318 -	fresh = TRUE;
   1.319 +	state->fresh = TRUE;
   1.320  
   1.321 -	for (i = 0; i < clear_code; ++i) {
   1.322 -	    table[0][i] = 0;
   1.323 -	    table[1][i] = i;
   1.324 +	for (i = 0; i < state->clear_code; ++i) {
   1.325 +	    state->table[0][i] = 0;
   1.326 +	    state->table[1][i] = i;
   1.327  	}
   1.328 -	table[1][0] = 0;
   1.329 +	state->table[1][0] = 0;
   1.330  	for (; i < (1 << MAX_LWZ_BITS); ++i)
   1.331 -	    table[0][i] = 0;
   1.332 +	    state->table[0][i] = 0;
   1.333  
   1.334 -	sp = stack;
   1.335 +	state->sp = state->stack;
   1.336  
   1.337  	return 0;
   1.338 -    } else if (fresh) {
   1.339 -	fresh = FALSE;
   1.340 +    } else if (state->fresh) {
   1.341 +	state->fresh = FALSE;
   1.342  	do {
   1.343 -	    firstcode = oldcode = GetCode(src, code_size, FALSE);
   1.344 -	} while (firstcode == clear_code);
   1.345 -	return firstcode;
   1.346 +	    state->firstcode = state->oldcode = GetCode(src, state->code_size, FALSE, state);
   1.347 +	} while (state->firstcode == state->clear_code);
   1.348 +	return state->firstcode;
   1.349      }
   1.350 -    if (sp > stack)
   1.351 -	return *--sp;
   1.352 +    if (state->sp > state->stack)
   1.353 +	return *--state->sp;
   1.354  
   1.355 -    while ((code = GetCode(src, code_size, FALSE)) >= 0) {
   1.356 -	if (code == clear_code) {
   1.357 -	    for (i = 0; i < clear_code; ++i) {
   1.358 -		table[0][i] = 0;
   1.359 -		table[1][i] = i;
   1.360 +    while ((code = GetCode(src, state->code_size, FALSE, state)) >= 0) {
   1.361 +	if (code == state->clear_code) {
   1.362 +	    for (i = 0; i < state->clear_code; ++i) {
   1.363 +		state->table[0][i] = 0;
   1.364 +		state->table[1][i] = i;
   1.365  	    }
   1.366  	    for (; i < (1 << MAX_LWZ_BITS); ++i)
   1.367 -		table[0][i] = table[1][i] = 0;
   1.368 -	    code_size = set_code_size + 1;
   1.369 -	    max_code_size = 2 * clear_code;
   1.370 -	    max_code = clear_code + 2;
   1.371 -	    sp = stack;
   1.372 -	    firstcode = oldcode = GetCode(src, code_size, FALSE);
   1.373 -	    return firstcode;
   1.374 -	} else if (code == end_code) {
   1.375 +	    state->table[0][i] = state->table[1][i] = 0;
   1.376 +	    state->code_size = state->set_code_size + 1;
   1.377 +	    state->max_code_size = 2 * state->clear_code;
   1.378 +	    state->max_code = state->clear_code + 2;
   1.379 +	    state->sp = state->stack;
   1.380 +	    state->firstcode = state->oldcode = GetCode(src, state->code_size, FALSE, state);
   1.381 +	    return state->firstcode;
   1.382 +	} else if (code == state->end_code) {
   1.383  	    int count;
   1.384  	    unsigned char buf[260];
   1.385  
   1.386 -	    if (ZeroDataBlock)
   1.387 +	    if (state->ZeroDataBlock)
   1.388  		return -2;
   1.389  
   1.390 -	    while ((count = GetDataBlock(src, buf)) > 0)
   1.391 +	    while ((count = GetDataBlock(src, buf, state)) > 0)
   1.392  		;
   1.393  
   1.394  	    if (count != 0) {
   1.395 @@ -489,22 +497,22 @@
   1.396  	}
   1.397  	incode = code;
   1.398  
   1.399 -	if (code >= max_code) {
   1.400 -	    *sp++ = firstcode;
   1.401 -	    code = oldcode;
   1.402 +	if (code >= state->max_code) {
   1.403 +	    *state->sp++ = state->firstcode;
   1.404 +	    code = state->oldcode;
   1.405  	}
   1.406 -	while (code >= clear_code) {
   1.407 +	while (code >= state->clear_code) {
   1.408  	    /* Guard against buffer overruns */
   1.409  	    if (code < 0 || code >= (1 << MAX_LWZ_BITS)) {
   1.410  		RWSetMsg("invalid LWZ data");
   1.411  		return -3;
   1.412  	    }
   1.413 -	    *sp++ = table[1][code];
   1.414 -	    if (code == table[0][code]) {
   1.415 +	    *state->sp++ = state->table[1][code];
   1.416 +	    if (code == state->table[0][code]) {
   1.417  		RWSetMsg("circular table entry BIG ERROR");
   1.418  		return -3;
   1.419  	    }
   1.420 -	    code = table[0][code];
   1.421 +	    code = state->table[0][code];
   1.422  	}
   1.423  
   1.424  	/* Guard against buffer overruns */
   1.425 @@ -512,22 +520,22 @@
   1.426  	    RWSetMsg("invalid LWZ data");
   1.427  	    return -4;
   1.428  	}
   1.429 -	*sp++ = firstcode = table[1][code];
   1.430 +	*state->sp++ = state->firstcode = state->table[1][code];
   1.431  
   1.432 -	if ((code = max_code) < (1 << MAX_LWZ_BITS)) {
   1.433 -	    table[0][code] = oldcode;
   1.434 -	    table[1][code] = firstcode;
   1.435 -	    ++max_code;
   1.436 -	    if ((max_code >= max_code_size) &&
   1.437 -		(max_code_size < (1 << MAX_LWZ_BITS))) {
   1.438 -		max_code_size *= 2;
   1.439 -		++code_size;
   1.440 +	if ((code = state->max_code) < (1 << MAX_LWZ_BITS)) {
   1.441 +	    state->table[0][code] = state->oldcode;
   1.442 +	    state->table[1][code] = state->firstcode;
   1.443 +	    ++state->max_code;
   1.444 +	    if ((state->max_code >= state->max_code_size) &&
   1.445 +		(state->max_code_size < (1 << MAX_LWZ_BITS))) {
   1.446 +		state->max_code_size *= 2;
   1.447 +		++state->code_size;
   1.448  	    }
   1.449  	}
   1.450 -	oldcode = incode;
   1.451 +	state->oldcode = incode;
   1.452  
   1.453 -	if (sp > stack)
   1.454 -	    return *--sp;
   1.455 +	if (state->sp > state->stack)
   1.456 +	    return *--state->sp;
   1.457      }
   1.458      return code;
   1.459  }
   1.460 @@ -535,7 +543,7 @@
   1.461  static Image *
   1.462  ReadImage(SDL_RWops * src, int len, int height, int cmapSize,
   1.463  	  unsigned char cmap[3][MAXCOLORMAPSIZE],
   1.464 -	  int gray, int interlace, int ignore)
   1.465 +	  int gray, int interlace, int ignore, State_t * state)
   1.466  {
   1.467      Image *image;
   1.468      unsigned char c;
   1.469 @@ -549,7 +557,7 @@
   1.470  	RWSetMsg("EOF / read error on image data");
   1.471  	return NULL;
   1.472      }
   1.473 -    if (LWZReadByte(src, TRUE, c) < 0) {
   1.474 +    if (LWZReadByte(src, TRUE, c, state) < 0) {
   1.475  	RWSetMsg("error reading image");
   1.476  	return NULL;
   1.477      }
   1.478 @@ -557,7 +565,7 @@
   1.479      **	If this is an "uninteresting picture" ignore it.
   1.480       */
   1.481      if (ignore) {
   1.482 -	while (LWZReadByte(src, FALSE, c) >= 0)
   1.483 +	while (LWZReadByte(src, FALSE, c, state) >= 0)
   1.484  	    ;
   1.485  	return NULL;
   1.486      }
   1.487 @@ -567,7 +575,7 @@
   1.488  	ImageSetCmap(image, i, cmap[CM_RED][i],
   1.489  		     cmap[CM_GREEN][i], cmap[CM_BLUE][i]);
   1.490  
   1.491 -    while ((v = LWZReadByte(src, FALSE, c)) >= 0) {
   1.492 +    while ((v = LWZReadByte(src, FALSE, c, state)) >= 0) {
   1.493  #ifdef USED_BY_SDL
   1.494  	((Uint8 *)image->pixels)[xpos + ypos * image->pitch] = v;
   1.495  #else