Skip to content
This repository has been archived by the owner on Feb 11, 2021. It is now read-only.

Latest commit

 

History

History
524 lines (440 loc) · 13.1 KB

SDL_test_fuzzer.c

File metadata and controls

524 lines (440 loc) · 13.1 KB
 
Nov 28, 2012
Nov 28, 2012
1
2
/*
Simple DirectMedia Layer
Feb 15, 2013
Feb 15, 2013
3
Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
Nov 28, 2012
Nov 28, 2012
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
May 18, 2013
May 18, 2013
22
/*
Nov 28, 2012
Nov 28, 2012
23
24
Data generators for fuzzing test data in a reproducible way.
May 18, 2013
May 18, 2013
25
Nov 28, 2012
Nov 28, 2012
26
27
28
29
*/
#include "SDL_config.h"
Dec 31, 2012
Dec 31, 2012
30
31
32
33
34
35
36
/* Visual Studio 2008 doesn't have stdint.h */
#if defined(_MSC_VER) && _MSC_VER <= 1500
#define UINT8_MAX ~(Uint8)0
#define UINT16_MAX ~(Uint16)0
#define UINT32_MAX ~(Uint32)0
#define UINT64_MAX ~(Uint64)0
#else
Nov 28, 2012
Nov 28, 2012
37
#include <stdint.h>
Dec 31, 2012
Dec 31, 2012
38
#endif
Nov 28, 2012
Nov 28, 2012
39
40
41
42
43
44
45
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <float.h>
#include "SDL_test.h"
May 18, 2013
May 18, 2013
46
/**
Feb 13, 2013
Feb 13, 2013
47
* Counter for fuzzer invocations
Nov 28, 2012
Nov 28, 2012
48
*/
Dec 16, 2012
Dec 16, 2012
49
static int fuzzerInvocationCounter = 0;
Nov 28, 2012
Nov 28, 2012
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
* Context for shared random number generator
*/
static SDLTest_RandomContext rndContext;
/*
* Note: doxygen documentation markup for functions is in the header file.
*/
void
SDLTest_FuzzerInit(Uint64 execKey)
{
May 18, 2013
May 18, 2013
63
64
65
66
67
Uint32 a = (execKey >> 32) & 0x00000000FFFFFFFF;
Uint32 b = execKey & 0x00000000FFFFFFFF;
SDL_memset((void *)&rndContext, 0, sizeof(SDLTest_RandomContext));
SDLTest_RandomInit(&rndContext, a, b);
fuzzerInvocationCounter = 0;
Nov 28, 2012
Nov 28, 2012
68
69
70
}
int
Dec 15, 2012
Dec 15, 2012
71
SDLTest_GetFuzzerInvocationCount()
Nov 28, 2012
Nov 28, 2012
72
{
May 18, 2013
May 18, 2013
73
return fuzzerInvocationCounter;
Nov 28, 2012
Nov 28, 2012
74
75
76
77
78
}
Uint8
SDLTest_RandomUint8()
{
May 18, 2013
May 18, 2013
79
fuzzerInvocationCounter++;
Nov 28, 2012
Nov 28, 2012
80
May 18, 2013
May 18, 2013
81
return (Uint8) SDLTest_RandomInt(&rndContext) & 0x000000FF;
Nov 28, 2012
Nov 28, 2012
82
83
84
85
86
}
Sint8
SDLTest_RandomSint8()
{
May 18, 2013
May 18, 2013
87
fuzzerInvocationCounter++;
Nov 28, 2012
Nov 28, 2012
88
May 18, 2013
May 18, 2013
89
return (Sint8) SDLTest_RandomInt(&rndContext) & 0x000000FF;
Nov 28, 2012
Nov 28, 2012
90
91
92
93
94
}
Uint16
SDLTest_RandomUint16()
{
May 18, 2013
May 18, 2013
95
fuzzerInvocationCounter++;
Nov 28, 2012
Nov 28, 2012
96
May 18, 2013
May 18, 2013
97
return (Uint16) SDLTest_RandomInt(&rndContext) & 0x0000FFFF;
Nov 28, 2012
Nov 28, 2012
98
99
100
101
102
}
Sint16
SDLTest_RandomSint16()
{
May 18, 2013
May 18, 2013
103
fuzzerInvocationCounter++;
Nov 28, 2012
Nov 28, 2012
104
May 18, 2013
May 18, 2013
105
return (Sint16) SDLTest_RandomInt(&rndContext) & 0x0000FFFF;
Nov 28, 2012
Nov 28, 2012
106
107
108
109
110
}
Sint32
SDLTest_RandomSint32()
{
May 18, 2013
May 18, 2013
111
fuzzerInvocationCounter++;
Nov 28, 2012
Nov 28, 2012
112
May 18, 2013
May 18, 2013
113
return (Sint32) SDLTest_RandomInt(&rndContext);
Nov 28, 2012
Nov 28, 2012
114
115
116
117
118
}
Uint32
SDLTest_RandomUint32()
{
May 18, 2013
May 18, 2013
119
fuzzerInvocationCounter++;
Nov 28, 2012
Nov 28, 2012
120
May 18, 2013
May 18, 2013
121
return (Uint32) SDLTest_RandomInt(&rndContext);
Nov 28, 2012
Nov 28, 2012
122
123
124
125
126
}
Uint64
SDLTest_RandomUint64()
{
May 18, 2013
May 18, 2013
127
128
Uint64 value = 0;
Uint32 *vp = (void *)&value;
Nov 28, 2012
Nov 28, 2012
129
May 18, 2013
May 18, 2013
130
fuzzerInvocationCounter++;
Nov 28, 2012
Nov 28, 2012
131
May 18, 2013
May 18, 2013
132
133
vp[0] = SDLTest_RandomSint32();
vp[1] = SDLTest_RandomSint32();
Nov 28, 2012
Nov 28, 2012
134
May 18, 2013
May 18, 2013
135
return value;
Nov 28, 2012
Nov 28, 2012
136
137
138
139
140
}
Sint64
SDLTest_RandomSint64()
{
May 18, 2013
May 18, 2013
141
142
Uint64 value = 0;
Uint32 *vp = (void *)&value;
Nov 28, 2012
Nov 28, 2012
143
May 18, 2013
May 18, 2013
144
fuzzerInvocationCounter++;
Nov 28, 2012
Nov 28, 2012
145
May 18, 2013
May 18, 2013
146
147
vp[0] = SDLTest_RandomSint32();
vp[1] = SDLTest_RandomSint32();
Nov 28, 2012
Nov 28, 2012
148
May 18, 2013
May 18, 2013
149
return value;
Nov 28, 2012
Nov 28, 2012
150
151
152
153
154
155
156
}
Sint32
SDLTest_RandomIntegerInRange(Sint32 pMin, Sint32 pMax)
{
May 18, 2013
May 18, 2013
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
Sint64 min = pMin;
Sint64 max = pMax;
Sint64 temp;
Sint64 number;
if(pMin > pMax) {
temp = min;
min = max;
max = temp;
} else if(pMin == pMax) {
return (Sint32)min;
}
number = SDLTest_RandomUint32();
/* invocation count increment in preceeding call */
return (Sint32)((number % ((max + 1) - min)) + min);
Nov 28, 2012
Nov 28, 2012
174
175
176
}
/*!
Feb 13, 2013
Feb 13, 2013
177
* Generates a unsigned boundary value between the given boundaries.
Nov 28, 2012
Nov 28, 2012
178
179
180
181
182
* Boundary values are inclusive. See the examples below.
* If boundary2 < boundary1, the values are swapped.
* If boundary1 == boundary2, value of boundary1 will be returned
*
* Generating boundary values for Uint8:
Feb 13, 2013
Feb 13, 2013
183
184
185
186
187
* BoundaryValues(UINT8_MAX, 10, 20, True) -> [10,11,19,20]
* BoundaryValues(UINT8_MAX, 10, 20, False) -> [9,21]
* BoundaryValues(UINT8_MAX, 0, 15, True) -> [0, 1, 14, 15]
* BoundaryValues(UINT8_MAX, 0, 15, False) -> [16]
* BoundaryValues(UINT8_MAX, 0, 0xFF, False) -> [0], error set
Nov 28, 2012
Nov 28, 2012
188
189
190
191
*
* Generator works the same for other types of unsigned integers.
*
* \param maxValue The biggest value that is acceptable for this data type.
May 18, 2013
May 18, 2013
192
* For instance, for Uint8 -> 255, Uint16 -> 65536 etc.
Feb 13, 2013
Feb 13, 2013
193
194
* \param boundary1 defines lower boundary
* \param boundary2 defines upper boundary
Nov 28, 2012
Nov 28, 2012
195
196
* \param validDomain Generate only for valid domain (for the data type)
*
Feb 13, 2013
Feb 13, 2013
197
* \returns Returns a random boundary value for the domain or 0 in case of error
Nov 28, 2012
Nov 28, 2012
198
*/
Feb 13, 2013
Feb 13, 2013
199
200
Uint64
SDLTest_GenerateUnsignedBoundaryValues(const Uint64 maxValue, Uint64 boundary1, Uint64 boundary2, SDL_bool validDomain)
Nov 28, 2012
Nov 28, 2012
201
{
Feb 13, 2013
Feb 13, 2013
202
Uint64 b1, b2;
May 18, 2013
May 18, 2013
203
204
205
206
Uint64 delta;
Uint64 tempBuf[4];
Uint8 index;
Feb 13, 2013
Feb 13, 2013
207
/* Maybe swap */
May 18, 2013
May 18, 2013
208
209
210
211
212
213
if (boundary1 > boundary2) {
b1 = boundary2;
b2 = boundary1;
} else {
b1 = boundary1;
b2 = boundary2;
Feb 13, 2013
Feb 13, 2013
214
}
Nov 28, 2012
Nov 28, 2012
215
May 18, 2013
May 18, 2013
216
217
218
219
220
221
222
223
224
225
226
227
index = 0;
if (validDomain == SDL_TRUE) {
if (b1 == b2) {
return b1;
}
/* Generate up to 4 values within bounds */
delta = b2 - b1;
if (delta < 4) {
do {
tempBuf[index] = b1 + index;
index++;
Feb 13, 2013
Feb 13, 2013
228
} while (index < delta);
May 18, 2013
May 18, 2013
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
} else {
tempBuf[index] = b1;
index++;
tempBuf[index] = b1 + 1;
index++;
tempBuf[index] = b2 - 1;
index++;
tempBuf[index] = b2;
index++;
}
} else {
/* Generate up to 2 values outside of bounds */
if (b1 > 0) {
tempBuf[index] = b1 - 1;
index++;
}
if (b2 < maxValue) {
tempBuf[index] = b2 + 1;
index++;
}
}
if (index == 0) {
/* There are no valid boundaries */
SDL_Unsupported();
return 0;
}
return tempBuf[SDLTest_RandomUint8() % index];
Nov 28, 2012
Nov 28, 2012
259
260
}
Feb 13, 2013
Feb 13, 2013
261
Nov 28, 2012
Nov 28, 2012
262
263
264
Uint8
SDLTest_RandomUint8BoundaryValue(Uint8 boundary1, Uint8 boundary2, SDL_bool validDomain)
{
May 18, 2013
May 18, 2013
265
266
267
268
269
/* max value for Uint8 */
const Uint64 maxValue = UCHAR_MAX;
return (Uint8)SDLTest_GenerateUnsignedBoundaryValues(maxValue,
(Uint64) boundary1, (Uint64) boundary2,
validDomain);
Nov 28, 2012
Nov 28, 2012
270
271
272
273
274
}
Uint16
SDLTest_RandomUint16BoundaryValue(Uint16 boundary1, Uint16 boundary2, SDL_bool validDomain)
{
May 18, 2013
May 18, 2013
275
276
277
278
279
/* max value for Uint16 */
const Uint64 maxValue = USHRT_MAX;
return (Uint16)SDLTest_GenerateUnsignedBoundaryValues(maxValue,
(Uint64) boundary1, (Uint64) boundary2,
validDomain);
Nov 28, 2012
Nov 28, 2012
280
281
282
283
284
}
Uint32
SDLTest_RandomUint32BoundaryValue(Uint32 boundary1, Uint32 boundary2, SDL_bool validDomain)
{
May 18, 2013
May 18, 2013
285
286
287
/* max value for Uint32 */
#if ((ULONG_MAX) == (UINT_MAX))
const Uint64 maxValue = ULONG_MAX;
May 12, 2013
May 12, 2013
288
#else
May 18, 2013
May 18, 2013
289
const Uint64 maxValue = UINT_MAX;
May 12, 2013
May 12, 2013
290
#endif
May 18, 2013
May 18, 2013
291
292
293
return (Uint32)SDLTest_GenerateUnsignedBoundaryValues(maxValue,
(Uint64) boundary1, (Uint64) boundary2,
validDomain);
Nov 28, 2012
Nov 28, 2012
294
295
296
297
298
}
Uint64
SDLTest_RandomUint64BoundaryValue(Uint64 boundary1, Uint64 boundary2, SDL_bool validDomain)
{
May 18, 2013
May 18, 2013
299
300
301
302
303
/* max value for Uint64 */
const Uint64 maxValue = ULLONG_MAX;
return SDLTest_GenerateUnsignedBoundaryValues(maxValue,
(Uint64) boundary1, (Uint64) boundary2,
validDomain);
Nov 28, 2012
Nov 28, 2012
304
305
306
}
/*!
Feb 13, 2013
Feb 13, 2013
307
* Generates a signed boundary value between the given boundaries.
Nov 28, 2012
Nov 28, 2012
308
309
310
311
312
* Boundary values are inclusive. See the examples below.
* If boundary2 < boundary1, the values are swapped.
* If boundary1 == boundary2, value of boundary1 will be returned
*
* Generating boundary values for Sint8:
Feb 13, 2013
Feb 13, 2013
313
314
315
316
317
* SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -10, 20, True) -> [-10,-9,19,20]
* SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -10, 20, False) -> [-11,21]
* SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -30, -15, True) -> [-30, -29, -16, -15]
* SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -127, 15, False) -> [16]
* SignedBoundaryValues(SCHAR_MIN, SCHAR_MAX, -127, 127, False) -> [0], error set
Nov 28, 2012
Nov 28, 2012
318
319
320
*
* Generator works the same for other types of signed integers.
*
Feb 13, 2013
Feb 13, 2013
321
* \param minValue The smallest value that is acceptable for this data type.
May 18, 2013
May 18, 2013
322
* For instance, for Uint8 -> -127, etc.
Nov 28, 2012
Nov 28, 2012
323
* \param maxValue The biggest value that is acceptable for this data type.
May 18, 2013
May 18, 2013
324
* For instance, for Uint8 -> 127, etc.
Feb 13, 2013
Feb 13, 2013
325
326
* \param boundary1 defines lower boundary
* \param boundary2 defines upper boundary
Nov 28, 2012
Nov 28, 2012
327
328
* \param validDomain Generate only for valid domain (for the data type)
*
Feb 13, 2013
Feb 13, 2013
329
* \returns Returns a random boundary value for the domain or 0 in case of error
Nov 28, 2012
Nov 28, 2012
330
*/
Feb 13, 2013
Feb 13, 2013
331
332
Sint64
SDLTest_GenerateSignedBoundaryValues(const Sint64 minValue, const Sint64 maxValue, Sint64 boundary1, Sint64 boundary2, SDL_bool validDomain)
Nov 28, 2012
Nov 28, 2012
333
{
Feb 13, 2013
Feb 13, 2013
334
Sint64 b1, b2;
May 18, 2013
May 18, 2013
335
336
337
338
Sint64 delta;
Sint64 tempBuf[4];
Uint8 index;
Feb 13, 2013
Feb 13, 2013
339
/* Maybe swap */
May 18, 2013
May 18, 2013
340
341
342
343
344
345
if (boundary1 > boundary2) {
b1 = boundary2;
b2 = boundary1;
} else {
b1 = boundary1;
b2 = boundary2;
Feb 13, 2013
Feb 13, 2013
346
}
Nov 28, 2012
Nov 28, 2012
347
May 18, 2013
May 18, 2013
348
349
350
351
352
353
354
355
356
357
358
359
index = 0;
if (validDomain == SDL_TRUE) {
if (b1 == b2) {
return b1;
}
/* Generate up to 4 values within bounds */
delta = b2 - b1;
if (delta < 4) {
do {
tempBuf[index] = b1 + index;
index++;
Feb 13, 2013
Feb 13, 2013
360
} while (index < delta);
May 18, 2013
May 18, 2013
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
} else {
tempBuf[index] = b1;
index++;
tempBuf[index] = b1 + 1;
index++;
tempBuf[index] = b2 - 1;
index++;
tempBuf[index] = b2;
index++;
}
} else {
/* Generate up to 2 values outside of bounds */
if (b1 > minValue) {
tempBuf[index] = b1 - 1;
index++;
}
if (b2 < maxValue) {
tempBuf[index] = b2 + 1;
index++;
}
}
if (index == 0) {
/* There are no valid boundaries */
SDL_Unsupported();
return minValue;
}
return tempBuf[SDLTest_RandomUint8() % index];
Nov 28, 2012
Nov 28, 2012
391
392
}
Feb 13, 2013
Feb 13, 2013
393
Nov 28, 2012
Nov 28, 2012
394
395
396
Sint8
SDLTest_RandomSint8BoundaryValue(Sint8 boundary1, Sint8 boundary2, SDL_bool validDomain)
{
May 18, 2013
May 18, 2013
397
398
399
400
401
402
/* min & max values for Sint8 */
const Sint64 maxValue = SCHAR_MAX;
const Sint64 minValue = SCHAR_MIN;
return (Sint8)SDLTest_GenerateSignedBoundaryValues(minValue, maxValue,
(Sint64) boundary1, (Sint64) boundary2,
validDomain);
Nov 28, 2012
Nov 28, 2012
403
404
405
406
407
}
Sint16
SDLTest_RandomSint16BoundaryValue(Sint16 boundary1, Sint16 boundary2, SDL_bool validDomain)
{
May 18, 2013
May 18, 2013
408
409
410
411
412
413
/* min & max values for Sint16 */
const Sint64 maxValue = SHRT_MAX;
const Sint64 minValue = SHRT_MIN;
return (Sint16)SDLTest_GenerateSignedBoundaryValues(minValue, maxValue,
(Sint64) boundary1, (Sint64) boundary2,
validDomain);
Nov 28, 2012
Nov 28, 2012
414
415
416
417
418
}
Sint32
SDLTest_RandomSint32BoundaryValue(Sint32 boundary1, Sint32 boundary2, SDL_bool validDomain)
{
May 18, 2013
May 18, 2013
419
420
421
422
/* min & max values for Sint32 */
#if ((ULONG_MAX) == (UINT_MAX))
const Sint64 maxValue = LONG_MAX;
const Sint64 minValue = LONG_MIN;
May 12, 2013
May 12, 2013
423
#else
May 18, 2013
May 18, 2013
424
425
const Sint64 maxValue = INT_MAX;
const Sint64 minValue = INT_MIN;
May 12, 2013
May 12, 2013
426
#endif
May 18, 2013
May 18, 2013
427
428
429
return (Sint32)SDLTest_GenerateSignedBoundaryValues(minValue, maxValue,
(Sint64) boundary1, (Sint64) boundary2,
validDomain);
Nov 28, 2012
Nov 28, 2012
430
431
432
433
434
}
Sint64
SDLTest_RandomSint64BoundaryValue(Sint64 boundary1, Sint64 boundary2, SDL_bool validDomain)
{
May 18, 2013
May 18, 2013
435
436
437
438
439
440
/* min & max values for Sint64 */
const Sint64 maxValue = LLONG_MAX;
const Sint64 minValue = LLONG_MIN;
return SDLTest_GenerateSignedBoundaryValues(minValue, maxValue,
boundary1, boundary2,
validDomain);
Nov 28, 2012
Nov 28, 2012
441
442
443
444
445
}
float
SDLTest_RandomUnitFloat()
{
May 18, 2013
May 18, 2013
446
return (float) SDLTest_RandomUint32() / UINT_MAX;
Nov 28, 2012
Nov 28, 2012
447
448
449
450
451
}
float
SDLTest_RandomFloat()
{
Feb 8, 2013
Feb 8, 2013
452
return (float) (SDLTest_RandomUnitDouble() * (double)2.0 * (double)FLT_MAX - (double)(FLT_MAX));
Nov 28, 2012
Nov 28, 2012
453
454
455
456
457
}
double
SDLTest_RandomUnitDouble()
{
May 18, 2013
May 18, 2013
458
return (double) (SDLTest_RandomUint64() >> 11) * (1.0/9007199254740992.0);
Nov 28, 2012
Nov 28, 2012
459
460
461
462
463
}
double
SDLTest_RandomDouble()
{
May 18, 2013
May 18, 2013
464
465
466
467
468
469
470
471
472
473
double r = 0.0;
double s = 1.0;
do {
s /= UINT_MAX + 1.0;
r += (double)SDLTest_RandomInt(&rndContext) * s;
} while (s > DBL_EPSILON);
fuzzerInvocationCounter++;
return r;
Nov 28, 2012
Nov 28, 2012
474
475
476
477
478
479
}
char *
SDLTest_RandomAsciiString()
{
May 18, 2013
May 18, 2013
480
return SDLTest_RandomAsciiStringWithMaximumLength(255);
Nov 28, 2012
Nov 28, 2012
481
482
483
}
char *
Jan 13, 2013
Jan 13, 2013
484
SDLTest_RandomAsciiStringWithMaximumLength(int maxLength)
Nov 28, 2012
Nov 28, 2012
485
{
May 18, 2013
May 18, 2013
486
int size;
Jan 13, 2013
Jan 13, 2013
487
May 18, 2013
May 18, 2013
488
if(maxLength < 1) {
Jan 13, 2013
Jan 13, 2013
489
SDL_InvalidParamError("maxLength");
May 18, 2013
May 18, 2013
490
491
492
493
return NULL;
}
size = (SDLTest_RandomUint32() % (maxLength + 1));
Jan 13, 2013
Jan 13, 2013
494
May 18, 2013
May 18, 2013
495
return SDLTest_RandomAsciiStringOfSize(size);
Jan 13, 2013
Jan 13, 2013
496
497
498
499
500
}
char *
SDLTest_RandomAsciiStringOfSize(int size)
{
May 18, 2013
May 18, 2013
501
502
char *string;
int counter;
Nov 28, 2012
Nov 28, 2012
503
504
May 18, 2013
May 18, 2013
505
if(size < 1) {
Jan 13, 2013
Jan 13, 2013
506
SDL_InvalidParamError("size");
May 18, 2013
May 18, 2013
507
508
return NULL;
}
Nov 28, 2012
Nov 28, 2012
509
May 18, 2013
May 18, 2013
510
511
512
string = (char *)SDL_malloc((size + 1) * sizeof(char));
if (string==NULL) {
return NULL;
Nov 28, 2012
Nov 28, 2012
513
514
}
May 18, 2013
May 18, 2013
515
516
517
for(counter = 0; counter < size; ++counter) {
string[counter] = (char)SDLTest_RandomIntegerInRange(32, 126);
}
Nov 28, 2012
Nov 28, 2012
518
May 18, 2013
May 18, 2013
519
string[counter] = '\0';
Nov 28, 2012
Nov 28, 2012
520
May 18, 2013
May 18, 2013
521
fuzzerInvocationCounter++;
Jan 13, 2013
Jan 13, 2013
522
May 18, 2013
May 18, 2013
523
return string;
Nov 28, 2012
Nov 28, 2012
524
}