📄 checkpoint.cc
字号:
#include <stdarg.h>#include <stdio.h>#include <string.h>#include <time.h>#include "checkpoint.hh"#include "inttypes.hh"#include "serial.hh"#include "sulima.hh"const char *Checkpoint::EOFError::what() const{ if (cp.fp && feof(cp.fp)) { // Premature end of file. Checkpoint::SyntaxError err(cp, "unexpected end of file"); return err.what(); } else { // A genuine file error. return FileError::what(); }}Checkpoint::SyntaxError::SyntaxError(Checkpoint &cp) : Error("Bad checkpoint file \"%s\" (line %d)", cp.name(), cp.lineno()){ // otherwise empty}Checkpoint::SyntaxError::SyntaxError(Checkpoint &cp, const char *cause, ...){ char buf[sizeof(msg)]; va_list ap; va_start(ap, cause); vecho(buf, sizeof(buf), cause, ap); va_end(ap); if (echo(msg, sizeof(msg), "Bad checkpoint file \"%s\" (line %d: %s)", cp.name(), cp.lineno(), buf) >= sizeof(msg)) { strcpy(msg + sizeof(msg) - 5, "..."); }}// Function used to implement 8 and 16-bit integer extractors. (max) is one// larger than the maximum return value. (sign) is true for signed types. The// delimiter character after the last digit is considered part of the integer.intCheckpoint::extract_int(int max, bool sign){ int c = getc(fp); bool neg; // Read the sign. if (!sign || c != '-') neg = false; else { neg = true; c = getc(fp); } // There must be at least one digit. if (c < '0' || c > '9') { if (c == EOF) throw EOFError(*this); else if (!sign) throw SyntaxError(*this, "an unsigned integer expected"); else { throw SyntaxError(*this, "an integer expected"); } } // Parse the integer. int x = 0; do { x = x * 10 + c - '0'; if (x >= max && (!neg || x > max)) throw SyntaxError(*this, "integer too large"); c = getc(fp); } while (c >= '0' && c <= '9'); if (c == EOF && ferror(fp)) throw EOFError(*this); return (neg ? -x : x);}longCheckpoint::extract_long(long max, bool sign){ int c = getc(fp); bool neg; // Read the sign. if (!sign || c != '-') neg = false; else { neg = true; c = getc(fp); } // There must be at least one digit. if (c < '0' || c > '9') { if (c == EOF) throw EOFError(*this); else if (!sign) throw SyntaxError(*this, "an unsigned integer expected"); else { throw SyntaxError(*this, "an integer expected"); } } // Parse the integer. long x = 0; do { x = x * 10 + c - '0'; if (x >= max && (!neg || x > max)) throw SyntaxError(*this, "integer too large"); c = getc(fp); } while (c >= '0' && c <= '9'); if (c == EOF && ferror(fp)) throw EOFError(*this); return (neg ? -x : x);}// String inserter, complicated by the need (?) to maintain line numbers.Checkpoint&Checkpoint::operator<<(const char *s){ fputs(s, fp); while ((s = strchr(s, '\n')) != 0) ++ln; return *this;}// Inserters for 64-bit integers. On 32 bit platforms, this is a bit of a hack// as C89 does not define a portable fprintf() format specifier for objects// larger than unsigned long.Checkpoint&Checkpoint::operator<<(UInt64 x){ if (sizeof(x) <= sizeof(unsigned long)) fprintf(fp, "%lu", (unsigned long)x); else { // Write x as multiple longs. const UInt64 block = 1000000000UL; unsigned long a = x % block; x /= block; unsigned long b = x % block; x /= block; unsigned long c = x; if (c) fprintf(fp, "%lu%09lu%09lu", c, b, a); else if (b) fprintf(fp, "%lu%09lu", b, a); else { fprintf(fp, "%lu", a); } } return *this;}Checkpoint&Checkpoint::operator<<(Int64 x){ if (sizeof(x) <= sizeof(long)) fprintf(fp, "%ld", (long)x); else { // Process the sign. UInt64 ux; if (x >= 0) ux = x; else { ux = -x; // |x| is a valid UInt64, even if x == -2^63. putc('-', fp); } // Write x as multiple longs. const UInt64 block = 1000000000UL; unsigned long a = ux % block; ux /= block; unsigned long b = ux % block; ux /= block; unsigned long c = ux; if (c) fprintf(fp, "%lu%09lu%09lu", c, b, a); else if (b) fprintf(fp, "%lu%09lu", b, a); else { fprintf(fp, "%lu", a); } } return *this;}// The below extractors are very unforgiving. No additional whitespaces are// tolerated before or after the token.Checkpoint&Checkpoint::operator>>(bool& b){ int c = getc(fp); switch (c) { case EOF: throw EOFError(*this); case '0': b = false; return *this; case '1': b = true; return *this; default: throw SyntaxError(*this, "'0' or '1' expected"); }}Checkpoint&Checkpoint::operator>>(char *&s){ // The returned string is delimited by any of the characters in (s). It // is dynamically allocated using new char[]. The returned string can be // empty. The delimiter is considered part of the input string, and is // replaced by '\0' in the result. char buffer[4096]; // temporary buffer used for short strings char *start = buffer, *p = start, *end = start + sizeof(buffer); int c, start_ln = lineno(); for (;;) { c = getc(fp); if (c == EOF) { if (ferror(fp)) throw EOFError(*this); else { // The delimiter is missing. ln = start_ln; throw SyntaxError(*this, "missing string delimiter (%#s)", s); } } if (c == '\n') ++ln; if (p == end) { // The buffer is too small, so double its size. size_t n = end - start; char *buf = new char[n * 2]; memcpy(buf, start, n); if (start != buffer) delete start; start = buf; p = start + n; end = start + n * 2; } if (!strchr(s, c)) *p++ = c; else { *p++ = '\0'; s = (start == buffer ? copy(start) : start); return *this; } }}Checkpoint&Checkpoint::operator>>(Int16& x){ if (sizeof(Int16) < sizeof(int)) x = extract_int(0x7fff + 1, true); else x = extract_long(0x7fffL + 1, true); return *this;}Checkpoint&Checkpoint::operator>>(Int32& x){ if (sizeof(Int32) < sizeof(long)) x = extract_int(0x7fffffffL + 1, true); else { Int64 y; *this >> y; if (y < -((Int64)1 << 31) || y > ((Int64)1 << 31) + 1) throw SyntaxError(*this, "integer too large"); x = y; } return *this;}Checkpoint&Checkpoint::operator>>(Int64& x){ int c = getc(fp); bool neg; // Read the sign. if (c != '-') neg = false; else { neg = true; c = getc(fp); } // There must be at least one digit. if (c < '0' || c > '9') { if (c == EOF) throw EOFError(*this); else { throw SyntaxError(*this, "an integer expected"); } } // Parse the integer. UInt64 y = 0; do { UInt64 old = y; y = y * 10 + c - '0'; if (old > y) throw SyntaxError(*this, "integer too large"); c = getc(fp); } while (c >= '0' && c <= '9'); if (c == EOF && ferror(fp)) throw EOFError(*this); if (neg) { if (y > ((UInt64)1 << 63)) throw SyntaxError(*this, "integer too large"); x = -y; } else { if (y > ((UInt64)1 << 63) - 1) throw SyntaxError(*this, "integer too large"); x = y; } return *this;}Checkpoint&Checkpoint::operator>>(UInt16& x){ if (sizeof(UInt16) < sizeof(int)) x = extract_int(0xffff + 1, false); else x = extract_long(0xffffL + 1, false); return *this;}Checkpoint&Checkpoint::operator>>(UInt32& x){ if (sizeof(Int32) < sizeof(long)) x = extract_int(0xffffffffL + 1, false); else { UInt64 y; *this >> y; if (y > ((UInt64)1 >> 32) - 1) throw SyntaxError(*this, "integer too large"); x = y; } return *this;}Checkpoint&Checkpoint::operator>>(UInt64& x){ int c = getc(fp); // There must be at least one digit. if (c < '0' || c > '9') { if (c == EOF) throw EOFError(*this); else { throw SyntaxError(*this, "an unsigned integer expected"); } } // Parse the integer. UInt64 y = 0; do { UInt64 old = y; y = y * 10 + c - '0'; if (old > y) throw SyntaxError(*this, "integer too large"); c = getc(fp); } while (c >= '0' && c <= '9'); if (c == EOF && ferror(fp)) throw EOFError(*this); x = y; return *this;}// Finally, the string pattern-matching extractor. It throw a SyntaxError// exception unless it can read the string (s) from the input.Checkpoint&Checkpoint::operator>>(const char *s){ const char *p = s; while (*p) { int c = getc(fp); if (c != *p) { if (c == EOF) throw EOFError(*this); else { throw SyntaxError(*this, "\"%#s\" expected", s); } } if (c == '\n') ++ln; ++p; } return *this;}// Create a checkpoint. Exactly one argument is expected (the file name).SimArgCheckpoint::create(const SimArgs &args){ // Check the arguments. if (args.length() == 0) throw Error("No checkpoint file name specified."); else if (args.length() > 1) throw Error("Too many arguments specified to \"checkpoint\"."); // Create the checkpoint object. Checkpoint cp(args[0]); // Open the file. if ((cp.fp = fopen(cp.name(), "w")) == 0) throw FileError(cp.name()); // Create the checkpoint. try { // Write the initial header. cp << sulima->banner << ' ' << "Checkpoint\n"; // Write the time stamp. time_t t; time(&t); cp << ctime(&t); // Checkpoint all serializable objects in the system. Note that the // empty line between objects is supressed if the previous object // didn't write any data. int prev_ln = -1; for (ObjectIterator i; i; ++i) { if (cp.ln > prev_ln) cp << '\n'; // a line separator i->checkpoint(cp, true); prev_ln = cp.ln; } // Close the file, detecting fflush errors. if (fclose(cp.fp)) throw FileError(cp.name()); } catch (...) { // Remove the partial file. fclose(cp.fp); remove(cp.name()); throw; } // We're done. return "";}// Restore a checkpoint. Exactly one argument is expected (the file name).SimArgCheckpoint::restore(const SimArgs &args){ // Check the arguments. if (args.length() == 0) throw Error("No checkpoint file name specified."); else if (args.length() > 1) throw Error("Too many arguments specified to \"restore\"."); // Create the checkpoint object. Checkpoint cp(args[0]); // Open the file. if ((cp.fp = fopen(cp.name(), "r")) == 0) throw FileError(cp.name()); // Read the checkpoint data. try { // Read the initial header. cp >> sulima->banner >> ' ' >> "Checkpoint\n"; // Read and display the time stamp. const char *timestamp = extract_line(cp); sulima->msg("Restoring checkpoint made on %s.\n", timestamp); delete timestamp; // Restore all objects from the file; while (!feof(cp.fp)) { // The first word of each object specifies its type. BasicSerialType *type; cp >> '\n' >> type; type->restore(cp); } // Close the file. fclose(cp.fp); } catch (...) { // Remove the partial file. fclose(cp.fp); throw; } // We're done. return "";}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -