diff options
| author | Nick Barnes | 2001-10-31 14:40:56 +0000 |
|---|---|---|
| committer | Nick Barnes | 2001-10-31 14:40:56 +0000 |
| commit | 7acfca905d76140f4cc0b09c9a12de237de364cd (patch) | |
| tree | 3ed8babfa3a73d30f29e08ca5d5adcda4ca4e826 /mps/code/mpm.c | |
| parent | b7ce4893f9902d57cd67ac9a92fa6c3d5a8fc833 (diff) | |
| download | emacs-7acfca905d76140f4cc0b09c9a12de237de364cd.tar.gz emacs-7acfca905d76140f4cc0b09c9a12de237de364cd.zip | |
Branch imports for masters.
Copied from Perforce
Change: 23678
ServerID: perforce.ravenbrook.com
Diffstat (limited to 'mps/code/mpm.c')
| -rw-r--r-- | mps/code/mpm.c | 525 |
1 files changed, 525 insertions, 0 deletions
diff --git a/mps/code/mpm.c b/mps/code/mpm.c new file mode 100644 index 00000000000..8f60f074882 --- /dev/null +++ b/mps/code/mpm.c | |||
| @@ -0,0 +1,525 @@ | |||
| 1 | /* impl.c.mpm: GENERAL MPM SUPPORT | ||
| 2 | * | ||
| 3 | * $HopeName: MMsrc!mpm.c(trunk.34) $ | ||
| 4 | * Copyright (C) 1996 Harlequin Limited. All rights reserved. | ||
| 5 | * | ||
| 6 | * .purpose: Miscellaneous support for the implementation of the MPM | ||
| 7 | * and pool classes. | ||
| 8 | * | ||
| 9 | * .sources: design.mps.writef */ | ||
| 10 | |||
| 11 | #include "mpm.h" | ||
| 12 | #include <stdarg.h> | ||
| 13 | /* Get some floating constants for WriteDouble */ | ||
| 14 | #include <float.h> | ||
| 15 | #include <limits.h> | ||
| 16 | |||
| 17 | |||
| 18 | SRCID(mpm, "$HopeName: MMsrc!mpm.c(trunk.34) $"); | ||
| 19 | |||
| 20 | |||
| 21 | /* MPMCheck -- test MPM assumptions */ | ||
| 22 | |||
| 23 | Bool MPMCheck(void) | ||
| 24 | { | ||
| 25 | CHECKL(sizeof(Word) * CHAR_BIT == MPS_WORD_WIDTH); | ||
| 26 | CHECKL(1uL << MPS_WORD_SHIFT == MPS_WORD_WIDTH); | ||
| 27 | CHECKL(AlignCheck(MPS_PF_ALIGN)); | ||
| 28 | /* Check that trace ids will fit in the TraceId type. */ | ||
| 29 | CHECKL(TraceLIMIT <= UINT_MAX); | ||
| 30 | /* Check that there are enough bits in */ | ||
| 31 | /* a TraceSet to store all possible trace ids. */ | ||
| 32 | CHECKL(sizeof(TraceSet) * CHAR_BIT >= TraceLIMIT); | ||
| 33 | |||
| 34 | CHECKL((SizeAlignUp(0, 2048) == 0)); | ||
| 35 | CHECKL(!SizeIsAligned(64, (unsigned) -1)); | ||
| 36 | CHECKL(SizeIsAligned(0, 32)); | ||
| 37 | CHECKL((SizeAlignUp(1024, 16) == 1024)); | ||
| 38 | /* .prime: 31051 is prime */ | ||
| 39 | CHECKL(SizeIsAligned(SizeAlignUp(31051, 256), 256)); | ||
| 40 | CHECKL(SizeIsAligned(SizeAlignUp(31051, 512), 512)); | ||
| 41 | CHECKL(!SizeIsAligned(31051, 1024)); | ||
| 42 | CHECKL(!SizeIsP2(0)); | ||
| 43 | CHECKL(SizeIsP2(128)); | ||
| 44 | CHECKL(SizeLog2(1L) == 0); | ||
| 45 | CHECKL(SizeLog2(256L) == 8); | ||
| 46 | CHECKL(SizeLog2(65536L) == 16); | ||
| 47 | CHECKL(SizeLog2(131072L) == 17); | ||
| 48 | |||
| 49 | /* .check.writef: We check that various types will fit in a Word; */ | ||
| 50 | /* See .writef.check. Don't need to check WriteFS or WriteFF as they */ | ||
| 51 | /* should not be cast to Word. */ | ||
| 52 | CHECKL(sizeof(WriteFA) <= sizeof(Word)); | ||
| 53 | CHECKL(sizeof(WriteFP) <= sizeof(Word)); | ||
| 54 | CHECKL(sizeof(WriteFW) <= sizeof(Word)); /* Should be trivial*/ | ||
| 55 | CHECKL(sizeof(WriteFU) <= sizeof(Word)); | ||
| 56 | CHECKL(sizeof(WriteFB) <= sizeof(Word)); | ||
| 57 | CHECKL(sizeof(WriteFC) <= sizeof(Word)); | ||
| 58 | /* .check.write.double: See .write.double.check */ | ||
| 59 | { | ||
| 60 | int e, DBL_EXP_DIG = 1; | ||
| 61 | for (e = DBL_MAX_10_EXP; e > 0; e /= 10) | ||
| 62 | DBL_EXP_DIG++; | ||
| 63 | CHECKL(DBL_EXP_DIG < DBL_DIG); | ||
| 64 | CHECKL(-(DBL_MIN_10_EXP) <= DBL_MAX_10_EXP); | ||
| 65 | } | ||
| 66 | |||
| 67 | return TRUE; | ||
| 68 | } | ||
| 69 | |||
| 70 | |||
| 71 | /* FunCheck -- check that a function pointer is valid */ | ||
| 72 | |||
| 73 | Bool FunCheck(Fun f) | ||
| 74 | { | ||
| 75 | CHECKL(f != NULL); | ||
| 76 | /* Could assert various platform-specific things here. */ | ||
| 77 | UNUSED(f); /* see .check.unused */ | ||
| 78 | return TRUE; | ||
| 79 | } | ||
| 80 | |||
| 81 | |||
| 82 | /* ShiftCheck -- check that a shift is valid */ | ||
| 83 | |||
| 84 | Bool ShiftCheck(Shift shift) | ||
| 85 | { | ||
| 86 | CHECKL(shift < MPS_WORD_WIDTH); /* standard.ansic 6.3.7 */ | ||
| 87 | UNUSED(shift); /* see .check.unused */ | ||
| 88 | return TRUE; | ||
| 89 | } | ||
| 90 | |||
| 91 | |||
| 92 | /* AttrCheck -- check that a set of pool attributes are valid */ | ||
| 93 | |||
| 94 | Bool AttrCheck(Attr attr) | ||
| 95 | { | ||
| 96 | CHECKL((attr & ~AttrMASK) == 0); | ||
| 97 | /* Could check for legal combinations of attributes. */ | ||
| 98 | UNUSED(attr); /* see .check.unused */ | ||
| 99 | return TRUE; | ||
| 100 | } | ||
| 101 | |||
| 102 | |||
| 103 | /* AlignCheck -- check that an alignment is valid */ | ||
| 104 | |||
| 105 | Bool AlignCheck(Align align) | ||
| 106 | { | ||
| 107 | CHECKL(align > 0 && (align & (align - 1)) == 0); | ||
| 108 | /* .check.unused: Check methods for signatureless types don't use */ | ||
| 109 | /* their argument in hot varieties, so UNUSED is needed. */ | ||
| 110 | UNUSED(align); | ||
| 111 | return TRUE; | ||
| 112 | } | ||
| 113 | |||
| 114 | |||
| 115 | /* WordIsAligned -- test whether a word is aligned */ | ||
| 116 | |||
| 117 | Bool (WordIsAligned)(Word word, Align align) | ||
| 118 | { | ||
| 119 | AVER(AlignCheck(align)); | ||
| 120 | return WordIsAligned(word, align); | ||
| 121 | } | ||
| 122 | |||
| 123 | |||
| 124 | /* WordAlignUp -- round a word up to the nearest aligned value */ | ||
| 125 | |||
| 126 | Word (WordAlignUp)(Word word, Align align) | ||
| 127 | { | ||
| 128 | AVER(AlignCheck(align)); | ||
| 129 | return WordAlignUp(word, align); | ||
| 130 | } | ||
| 131 | |||
| 132 | /* WordRoundUp -- round word up to round. | ||
| 133 | * | ||
| 134 | * .wordroundup.arg.word: The word arg is quantity to be rounded. | ||
| 135 | * .wordroundup.arg.round: The modulus argument is not necessarily an | ||
| 136 | * alignment (i.e., not a power of two). | ||
| 137 | * | ||
| 138 | * .wordroundup.result: Let m be congruent to 0 mod r (m == 0(r)), and | ||
| 139 | * let m be the least m >= w. If w+r-1 (!) is representable in Word | ||
| 140 | * then result is m. Otherwise result is 0. Wittily. (NB. Result may | ||
| 141 | * be 0 even if m is representable.) */ | ||
| 142 | |||
| 143 | Word (WordRoundUp)(Word word, Size modulus) | ||
| 144 | { | ||
| 145 | AVER(modulus > 0); | ||
| 146 | return WordRoundUp(word, modulus); | ||
| 147 | } | ||
| 148 | |||
| 149 | |||
| 150 | /* WordAlignUp -- round a word down to the nearest aligned value */ | ||
| 151 | |||
| 152 | Word (WordAlignDown)(Word word, Align alignment) | ||
| 153 | { | ||
| 154 | AVER(AlignCheck(alignment)); | ||
| 155 | return WordAlignDown(word, alignment); | ||
| 156 | } | ||
| 157 | |||
| 158 | |||
| 159 | /* SizeIsP2 -- test whether a size is a power of two */ | ||
| 160 | |||
| 161 | Bool SizeIsP2(Size size) | ||
| 162 | { | ||
| 163 | return size > 0 && (size & (size - 1)) == 0; | ||
| 164 | } | ||
| 165 | |||
| 166 | |||
| 167 | /* Logarithms */ | ||
| 168 | |||
| 169 | Shift SizeFloorLog2(Size size) | ||
| 170 | { | ||
| 171 | Shift l = 0; | ||
| 172 | |||
| 173 | AVER(size != 0); | ||
| 174 | while(size > 1) { | ||
| 175 | ++l; | ||
| 176 | size >>= 1; | ||
| 177 | } | ||
| 178 | return l; | ||
| 179 | } | ||
| 180 | |||
| 181 | Shift SizeLog2(Size size) | ||
| 182 | { | ||
| 183 | AVER(SizeIsP2(size)); | ||
| 184 | return SizeFloorLog2(size); | ||
| 185 | } | ||
| 186 | |||
| 187 | |||
| 188 | /* AddrAlignDown -- round a word down to the nearest aligned value */ | ||
| 189 | |||
| 190 | Addr (AddrAlignDown)(Addr addr, Align alignment) | ||
| 191 | { | ||
| 192 | AVER(AlignCheck(alignment)); | ||
| 193 | return AddrAlignDown(addr, alignment); | ||
| 194 | } | ||
| 195 | |||
| 196 | |||
| 197 | /* ResIsAllocFailure | ||
| 198 | * | ||
| 199 | * Test whether a result code is in the set of allocation failure codes. */ | ||
| 200 | |||
| 201 | Bool ResIsAllocFailure(Res res) | ||
| 202 | { | ||
| 203 | return (res == ResMEMORY || res == ResRESOURCE || res == ResCOMMIT_LIMIT); | ||
| 204 | } | ||
| 205 | |||
| 206 | |||
| 207 | /* WriteWord -- output a textual representation of a word to a stream | ||
| 208 | * | ||
| 209 | * Output as an unsigned value in the given base (2-16), padded to the | ||
| 210 | * given width. */ | ||
| 211 | |||
| 212 | static Res WriteWord(mps_lib_FILE *stream, Word w, unsigned base, | ||
| 213 | unsigned width) | ||
| 214 | { | ||
| 215 | static const char digit[16] = "0123456789ABCDEF"; | ||
| 216 | static const char pad = '0'; /* padding character */ | ||
| 217 | char buf[MPS_WORD_WIDTH + 1]; /* enough for binary, */ | ||
| 218 | /* plus one for terminator */ | ||
| 219 | unsigned i; | ||
| 220 | int r; | ||
| 221 | |||
| 222 | AVER(stream != NULL); | ||
| 223 | AVER(2 <= base && base <= 16); | ||
| 224 | AVER(width <= MPS_WORD_WIDTH); | ||
| 225 | |||
| 226 | /* Add digits to the buffer starting at the right-hand end, so that */ | ||
| 227 | /* the buffer forms a string representing the number. A do...while */ | ||
| 228 | /* loop is used to ensure that at least one digit (zero) is written */ | ||
| 229 | /* when the number is zero. */ | ||
| 230 | i = MPS_WORD_WIDTH; | ||
| 231 | buf[i] = '\0'; | ||
| 232 | do { | ||
| 233 | --i; | ||
| 234 | buf[i] = digit[w % base]; | ||
| 235 | w /= base; | ||
| 236 | } while(w > 0); | ||
| 237 | |||
| 238 | /* If the number is not as wide as the requested field, pad out the */ | ||
| 239 | /* buffer with zeros. */ | ||
| 240 | while(i > MPS_WORD_WIDTH - width) { | ||
| 241 | --i; | ||
| 242 | buf[i] = pad; | ||
| 243 | } | ||
| 244 | |||
| 245 | r = mps_lib_fputs(&buf[i], stream); | ||
| 246 | if (r == mps_lib_EOF) | ||
| 247 | return ResIO; | ||
| 248 | |||
| 249 | return ResOK; | ||
| 250 | } | ||
| 251 | |||
| 252 | |||
| 253 | /* WriteDouble -- write a double float to a stream | ||
| 254 | * | ||
| 255 | * Cf.: Guy L. Steele, Jr. and Jon L. White, "How to print | ||
| 256 | * floating-point numbers accurately", ACM SIGPLAN Notices, Vol. 25, | ||
| 257 | * No. 6 (Jun. 1990), Pages 112-126 | ||
| 258 | * | ||
| 259 | * .write.double.limitation: Only the "simple" printer is implemented | ||
| 260 | * here. | ||
| 261 | * | ||
| 262 | * .write.double.check: There being no DBL_EXP_DIG, we assume that it is | ||
| 263 | * less than DBL_DIG. */ | ||
| 264 | |||
| 265 | static Res WriteDouble(mps_lib_FILE *stream, double d) | ||
| 266 | { | ||
| 267 | double F = d; | ||
| 268 | int E = 0, i, x = 0; | ||
| 269 | /* Largest exponent that will print in %f style. Larger will use %e */ | ||
| 270 | /* style. DBL_DIG is chosen for use of doubles as extra-large integers. */ | ||
| 271 | int expmax = DBL_DIG; | ||
| 272 | /* Smallest exponent that will print in %f style. Smaller will use */ | ||
| 273 | /* %e style. -4 is chosen because it is the %g default. */ | ||
| 274 | int expmin = -4; | ||
| 275 | /* Epsilon defines how many digits will be printed. Using DBL_EPSILON */ | ||
| 276 | /* prints all the significant digits. To print fewer digits, set */ | ||
| 277 | /* epsilon to 10 ^ - N, where N is the desired number of digits. */ | ||
| 278 | double epsilon = DBL_EPSILON / 2; | ||
| 279 | char digits[] = "0123456789"; | ||
| 280 | /* sign, DBL_DIG, '0.', 'e', '+/-', log10(DBL_MAX_10_EXP), */ | ||
| 281 | /* terminator. See .write.double.check. */ | ||
| 282 | char buf[1+DBL_DIG+2+1+1+DBL_DIG+1]; | ||
| 283 | int j = 0; | ||
| 284 | |||
| 285 | if (F == 0.0) { | ||
| 286 | if (mps_lib_fputs("0", stream) == mps_lib_EOF) | ||
| 287 | return ResIO; | ||
| 288 | return ResOK; | ||
| 289 | } | ||
| 290 | |||
| 291 | if (F < 0) { | ||
| 292 | buf[j] = '-'; | ||
| 293 | j++; | ||
| 294 | F = - F; | ||
| 295 | } | ||
| 296 | |||
| 297 | /* This scaling operation could introduce rounding errors. */ | ||
| 298 | for ( ; F >= 1.0 ; F /= 10.0) { | ||
| 299 | E++; | ||
| 300 | if (E > DBL_MAX_10_EXP) { | ||
| 301 | if (mps_lib_fputs("Infinity", stream) == mps_lib_EOF) | ||
| 302 | return ResIO; | ||
| 303 | return ResOK; | ||
| 304 | } | ||
| 305 | } | ||
| 306 | for ( ; F < 0.1; F *= 10) | ||
| 307 | E--; | ||
| 308 | |||
| 309 | /* See if %e notation is required */ | ||
| 310 | if (E > expmax || E <= expmin) { | ||
| 311 | x = E - 1; | ||
| 312 | E = 1; | ||
| 313 | } | ||
| 314 | |||
| 315 | /* Insert leading 0's */ | ||
| 316 | if (E <= 0) { | ||
| 317 | buf[j] = '0'; | ||
| 318 | j++; | ||
| 319 | } | ||
| 320 | if (E < 0) { | ||
| 321 | buf[j] = '.'; | ||
| 322 | j++; | ||
| 323 | } | ||
| 324 | for (i = -E; i > 0; i--) { | ||
| 325 | buf[j] = '0'; | ||
| 326 | j++; | ||
| 327 | } | ||
| 328 | |||
| 329 | /* Convert the fraction to base 10, inserting a decimal according to */ | ||
| 330 | /* the exponent. This is Steele and White's FP3 algorithm. */ | ||
| 331 | do { | ||
| 332 | int U; | ||
| 333 | |||
| 334 | if (E == 0) { | ||
| 335 | buf[j] = '.'; | ||
| 336 | j++; | ||
| 337 | } | ||
| 338 | F *= 10.0; | ||
| 339 | U = (int)F; | ||
| 340 | F = F - U; | ||
| 341 | epsilon *= 10.0; | ||
| 342 | E--; | ||
| 343 | if (F < epsilon || F > 1.0 - epsilon) { | ||
| 344 | if (F < 0.5) | ||
| 345 | buf[j] = digits[U]; | ||
| 346 | else | ||
| 347 | buf[j] = digits[U + 1]; | ||
| 348 | j++; | ||
| 349 | break; | ||
| 350 | } | ||
| 351 | buf[j] = digits[U]; | ||
| 352 | j++; | ||
| 353 | } while (1); | ||
| 354 | |||
| 355 | /* Insert trailing 0's */ | ||
| 356 | for (i = E; i > 0; i--) { | ||
| 357 | buf[j] = '0'; | ||
| 358 | j++; | ||
| 359 | } | ||
| 360 | |||
| 361 | /* If %e notation is selected, append the exponent indicator and sign. */ | ||
| 362 | if (x != 0) { | ||
| 363 | buf[j] = 'e'; | ||
| 364 | j++; | ||
| 365 | if (x < 0) { | ||
| 366 | buf[j] = '-'; | ||
| 367 | j++; | ||
| 368 | x = - x; | ||
| 369 | } | ||
| 370 | else { | ||
| 371 | buf[j] = '+'; | ||
| 372 | j++; | ||
| 373 | } | ||
| 374 | |||
| 375 | /* Format the exponent to at least two digits. */ | ||
| 376 | for (i = 100; i <= x; ) | ||
| 377 | i *= 10; | ||
| 378 | i /= 10; | ||
| 379 | do { | ||
| 380 | buf[j] = digits[x / i]; | ||
| 381 | j++; | ||
| 382 | x %= i; | ||
| 383 | i /= 10; | ||
| 384 | } while (i > 0); | ||
| 385 | } | ||
| 386 | buf[j] = '\0'; /* arnold */ | ||
| 387 | |||
| 388 | if (mps_lib_fputs(buf, stream) == mps_lib_EOF) | ||
| 389 | return ResIO; | ||
| 390 | return ResOK; | ||
| 391 | } | ||
| 392 | |||
| 393 | |||
| 394 | /* WriteF -- write formatted output | ||
| 395 | * | ||
| 396 | * .writef.des: See design.mps.writef, also design.mps.lib | ||
| 397 | * | ||
| 398 | * .writef.p: There is an assumption that void * fits in Word in | ||
| 399 | * the case of $P, and unsigned long for $U and $B. This is checked in | ||
| 400 | * MPMCheck. | ||
| 401 | * | ||
| 402 | * .writef.div: Although MPS_WORD_WIDTH/4 appears three times, there | ||
| 403 | * are effectively three separate decisions to format at this width. | ||
| 404 | * | ||
| 405 | * .writef.check: See .check.writef. */ | ||
| 406 | |||
| 407 | Res WriteF(mps_lib_FILE *stream, ...) | ||
| 408 | { | ||
| 409 | const char *format; | ||
| 410 | int r; | ||
| 411 | size_t i; | ||
| 412 | Res res; | ||
| 413 | va_list args; | ||
| 414 | |||
| 415 | AVER(stream != NULL); | ||
| 416 | |||
| 417 | va_start(args, stream); | ||
| 418 | |||
| 419 | for(;;) { | ||
| 420 | format = va_arg(args, const char *); | ||
| 421 | if (format == NULL) | ||
| 422 | break; | ||
| 423 | |||
| 424 | while(*format != '\0') { | ||
| 425 | if (*format != '$') { | ||
| 426 | r = mps_lib_fputc(*format, stream); /* Could be more efficient */ | ||
| 427 | if (r == mps_lib_EOF) return ResIO; | ||
| 428 | } else { | ||
| 429 | ++format; | ||
| 430 | AVER(*format != '\0'); | ||
| 431 | |||
| 432 | switch(*format) { | ||
| 433 | case 'A': { /* address */ | ||
| 434 | WriteFA addr = va_arg(args, WriteFA); | ||
| 435 | res = WriteWord(stream, (Word)addr, 16, | ||
| 436 | (sizeof(WriteFA) * CHAR_BIT + 3) / 4); | ||
| 437 | if (res != ResOK) return res; | ||
| 438 | } break; | ||
| 439 | |||
| 440 | case 'P': { /* pointer, see .writef.p */ | ||
| 441 | WriteFP p = va_arg(args, WriteFP); | ||
| 442 | res = WriteWord(stream, (Word)p, 16, | ||
| 443 | (sizeof(WriteFP) * CHAR_BIT + 3)/ 4); | ||
| 444 | if (res != ResOK) return res; | ||
| 445 | } break; | ||
| 446 | |||
| 447 | case 'F': { /* function */ | ||
| 448 | WriteFF f = va_arg(args, WriteFF); | ||
| 449 | Byte *b = (Byte *)&f; | ||
| 450 | for(i=0; i < sizeof(WriteFF); i++) { | ||
| 451 | res = WriteWord(stream, (Word)(b[i]), 16, | ||
| 452 | (CHAR_BIT + 3) / 4); | ||
| 453 | if (res != ResOK) return res; | ||
| 454 | } | ||
| 455 | } break; | ||
| 456 | |||
| 457 | case 'S': { /* string */ | ||
| 458 | WriteFS s = va_arg(args, WriteFS); | ||
| 459 | r = mps_lib_fputs((const char *)s, stream); | ||
| 460 | if (r == mps_lib_EOF) return ResIO; | ||
| 461 | } break; | ||
| 462 | |||
| 463 | case 'C': { /* character */ | ||
| 464 | WriteFC c = va_arg(args, WriteFC); /* promoted */ | ||
| 465 | r = mps_lib_fputc((int)c, stream); | ||
| 466 | if (r == mps_lib_EOF) return ResIO; | ||
| 467 | } break; | ||
| 468 | |||
| 469 | case 'W': { /* word */ | ||
| 470 | WriteFW w = va_arg(args, WriteFW); | ||
| 471 | res = WriteWord(stream, (Word)w, 16, | ||
| 472 | (sizeof(WriteFW) * CHAR_BIT + 3) / 4); | ||
| 473 | if (res != ResOK) return res; | ||
| 474 | } break; | ||
| 475 | |||
| 476 | case 'U': { /* decimal, see .writef.p */ | ||
| 477 | WriteFU u = va_arg(args, WriteFU); | ||
| 478 | res = WriteWord(stream, (Word)u, 10, 0); | ||
| 479 | if (res != ResOK) return res; | ||
| 480 | } break; | ||
| 481 | |||
| 482 | case 'B': { /* binary, see .writef.p */ | ||
| 483 | WriteFB b = va_arg(args, WriteFB); | ||
| 484 | res = WriteWord(stream, (Word)b, 2, sizeof(WriteFB) * CHAR_BIT); | ||
| 485 | if (res != ResOK) return res; | ||
| 486 | } break; | ||
| 487 | |||
| 488 | case '$': { /* dollar char */ | ||
| 489 | r = mps_lib_fputc('$', stream); | ||
| 490 | if (r == mps_lib_EOF) return ResIO; | ||
| 491 | } break; | ||
| 492 | |||
| 493 | case 'D': { /* double */ | ||
| 494 | WriteFD d = va_arg(args, WriteFD); | ||
| 495 | res = WriteDouble(stream, d); | ||
| 496 | if (res != ResOK) return res; | ||
| 497 | } break; | ||
| 498 | |||
| 499 | default: | ||
| 500 | NOTREACHED; | ||
| 501 | } | ||
| 502 | } | ||
| 503 | |||
| 504 | ++format; | ||
| 505 | } | ||
| 506 | } | ||
| 507 | |||
| 508 | va_end(args); | ||
| 509 | |||
| 510 | return ResOK; | ||
| 511 | } | ||
| 512 | |||
| 513 | |||
| 514 | /* StringLength -- Slow substitute for strlen */ | ||
| 515 | |||
| 516 | size_t StringLength(const char *s) | ||
| 517 | { | ||
| 518 | size_t i; | ||
| 519 | |||
| 520 | AVER(s != NULL); | ||
| 521 | |||
| 522 | for(i = 0; s[i] != '\0'; i++) | ||
| 523 | NOOP; | ||
| 524 | return(i); | ||
| 525 | } | ||