LevelDB 源码阅读之 Status

Status 位于 Slice 同级目录下

Status 的定义在 include/leveldb/status.h 里,在 util/status.cc 为 Status 的实现

成员变量

const char* state_;

enum Code {
  kOk = 0,
  kNotFound = 1,
  kCorruption = 2,
  kNotSupported = 3,
  kInvalidArgument = 4,
  kIOError = 5
};

Status 只有一个成员变量 state_,该成员变量是一个 char 指针,并且一经初始化便不能再改变。state_ 存储了所有和状态相关的信息,用 state_[0...3] 来表示其中包含的 message 的长度,用 state_[4] 来表示状态码,用 state_[5...] 之后的空间存储 message。

因为只用了一个字节来表示状态码,所以 Status 能表示的状态数量不超过 2^8 个。在 Status 内部,使用一个枚举值来表示可能存在的状态,主要有:kOk、kNotFound、kCorruption、kNotSupported、kInvalidArgument 和 kIOError 等六种状态码。

为了方便,Status 自己定义了一个私有的成员函数来从 state_ 中获取状态码:

Code code() const {
  return (state_ == NULL) ? kOk : static_cast<Code>(state_[4]);
}

可以看出,当 state_ 为 NULL 的时候,默认的状态码是 kOk,否则的话就直接取 state_[4] 里的值作为状态码

虽然总共有六中状态,但是 Status 却只提供了其中四种状态的判断接口,他们分别是 kOk、kNotFound、kCorruption 和 kIOError:

// Returns true iff the status indicates success.
bool ok() const { return (state_ == NULL); }

// Returns true iff the status indicates a NotFound error.
bool IsNotFound() const { return code() == kNotFound; }

// Returns true iff the status indicates a Corruption error.
bool IsCorruption() const { return code() == kCorruption; }

// Returns true iff the status indicates an IOError.
bool IsIOError() const { return code() == kIOError; }

构造和析构函数

Status 对外提供了两个构造函数:

Status() : state_(NULL) { }
Status(const Status& s);

默认构造函数直接构造一个状态为 kOk 的Status,另外,提供了一个拷贝构造函数。

此外,Status 分别提供了构造所有六种状态的 Status 类方法:

// Return a success status.
static Status OK() { return Status(); }

// Return error status of an appropriate type.
static Status NotFound(const Slice& msg, const Slice& msg2 = Slice()) {
  return Status(kNotFound, msg, msg2);
}
static Status Corruption(const Slice& msg, const Slice& msg2 = Slice()) {
  return Status(kCorruption, msg, msg2);
}
static Status NotSupported(const Slice& msg, const Slice& msg2 = Slice()) {
  return Status(kNotSupported, msg, msg2);
}
static Status InvalidArgument(const Slice& msg, const Slice& msg2 = Slice()) {
  return Status(kInvalidArgument, msg, msg2);
}
static Status IOError(const Slice& msg, const Slice& msg2 = Slice()) {
  return Status(kIOError, msg, msg2);
}

它们都使用到了一个私有的构造函数:

Status(Code code, const Slice& msg, const Slice& msg2);

它的实现如下:

Status::Status(Code code, const Slice& msg, const Slice& msg2) {
  assert(code != kOk);
  const uint32_t len1 = msg.size();
  const uint32_t len2 = msg2.size();
  const uint32_t size = len1 + (len2 ? (2 + len2) : 0);
  char* result = new char[size + 5];
  memcpy(result, &size, sizeof(size));
  result[4] = static_cast<char>(code);
  memcpy(result + 5, msg.data(), len1);
  if (len2) {
    result[5 + len1] = ':';
    result[6 + len1] = ' ';
    memcpy(result + 7 + len1, msg2.data(), len2);
  }
  state_ = result;
}

可以看到,该构造函数的意思是这样:构造一个非 kOk 状态的 Status,如果 msg2 为空,那么该 Status 的 Error Message 就是 msg 的内容,否则,该 Status 的 Error Message 的格式为:"msg: msg2",也就是说,认为 msg 是一个字段,而 msg2 是该字段的值。也可以很明显的看到,新建这个 Status 的时候,会从堆中分配一段内存用以存储 message 信息。

结合上面分别创建的五个 Error Status 来看,在创建他们的时候,Status 对象本身不会发生改变,变化的是其内部 Message 指针的值,分别调用那五个函数,其根本是在堆上分配一段内存用来存储 Error Code 和 Message 信息,然后把内部指针指向这段内存

重载函数

在 Status 仅对 '=' 进行了重载。

inline void Status::operator=(const Status& s) {
  // The following condition catches both aliasing (when this == &s),
  // and the common case where both s and *this are ok.
  if (state_ != s.state_) {
    delete[] state_;
    state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_);
  }
}

这里用到了另一个私有的成员函数 static const char* CopyState(const char* s);,先来看看它:

const char* Status::CopyState(const char* state) {
  uint32_t size;
  memcpy(&size, state, sizeof(size));
  char* result = new char[size + 5];
  memcpy(result, state, size + 5);
  return result;
}

这个拷贝的过程比较简单:先获得整个 Error Message 的长度 size,然后从堆里申请 size + 5 字节的空间,最后把整个内容拷贝到这个新申请的空间中,最后把这段空间的首地址返回。

而对 '=' 的重载过程中,如果发现两个 Status 指向的 Message 地址不一样,先把自己原来的 Message 从堆里释放掉,然后执行拷贝动作(从堆里申请内存,拷贝 Error Message),最后把新的地址赋值给当前的 Message 指针。

其他函数

最后我们还剩下一个了:

std::string Status::ToString() const {
  if (state_ == NULL) {
    return "OK";
  } else {
    char tmp[30];
    const char* type;
    switch (code()) {
      case kOk:
        type = "OK";
        break;
      case kNotFound:
        type = "NotFound: ";
        break;
      case kCorruption:
        type = "Corruption: ";
        break;
      case kNotSupported:
        type = "Not implemented: ";
        break;
      case kInvalidArgument:
        type = "Invalid argument: ";
        break;
      case kIOError:
        type = "IO error: ";
        break;
      default:
        snprintf(tmp, sizeof(tmp), "Unknown code(%d): ",
                 static_cast<int>(code()));
        type = tmp;
        break;
    }
    std::string result(type);
    uint32_t length;
    memcpy(&length, state_, sizeof(length));
    result.append(state_ + 5, length);
    return result;
  }
}

其实 ToString() 的功能比较简单:返回 Error Code 对应的 Error Message,具体格式可以看代码

你可能感兴趣的:(LevelDB 源码阅读之 Status)