【LevelDB源码阅读】Status

是什么

status用来判断函数返回的状态信息,封装了错误码和错误信息。

为什么要用

  • 为了便于管理和定位错误类型,一般大型系统都自定义自己的函数返回状态信息

学到什么

  • void *memcpy(void *str1, const void *str2, size_t n):从存储区 str2 复制 n 个字节到存储区 str1
  • 使用枚举类型定义多种状态
  • 字符串可以分为多个部分,不同部分描述不同内容
  • 通过static成员函数构造对象
  • noexcept使用
  • 格式化字符串snprint
  • 字符串追加s.append(args)

源码分析

将不同返回值码定义为一个枚举类型。

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

枚举类型本质是一种int类型,4个字节,上述枚举类型定义不占用内存空间,只有当定义枚举类型变量时才占用空间,如: Code code,code只能使用声明中列出的字符串来初始化。

Status本质就一个成员变量const char *state_,为了节省内存,state_分为三部分使用:

  • state_[0..3] == length of message
  • state_[4] == code
  • state_[5..] == message

拷贝控制
关键字noexcept声明符表明该函数不会抛出异常。
主要优点:

  • 知道函数不会抛出异常有助于简化调用该函数代码
  • 编译器可以执行某些特殊优化操作
  // Create a success status.
  Status() noexcept : state_(nullptr) {}
  ~Status() { delete[] state_; }

  Status(const Status &rhs);
  Status& operator=(const Status &rhs);

  Status(Status &&rhs) noexcept : state_(rhs.state_) { rhs.state_ = nullptr; }
  Status& operator=(Status &&rhs) noexcept;

具体实现

inline Status::Status(const Status &rhs) {
  state_ = (rhs.state_ == nullptr) ? nullptr : CopyState(rhs.state_);
}
inline Status& Status::operator=(const Status &rhs) {
  if (state_ != rhs.state_) {
    delete[] state_;
    state_ = (rhs.state_ == nullptr) ? nullptr : CopyState(rhs.state_);
  }
  return *this;
}
inline Status& Status::operator=(Status &&rhs) noexcept {
  std::swap(state_, rhs.state_);
  return *this;
}

其中拷贝构造和拷贝赋值本质都是调用CopyState来完成,重新开辟一块内存存储s.state_并将指针赋给调用函数的s.state_,CopyState实现如下:

const char* Status::CopyState(const char *state) {
  uint32_t size;
  memcpy(&size, state, sizeof(size));  // 复制长度到size
  char *result = new char[size + 5];  // 结果总长度包括前面5个字节
  memcpy(result, state, size + 5);  // 复制完整state内容,长度为size + 5
  return result;
}

主要对外接口
直接返回特定类型对象

  // 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 s;
s = Status::Corruption("corrupted key for ", user_key);  // 会用到赋值操作符

这几个函数又直接调用以下函数

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 = static_cast<uint32_t>(msg.size());
  const uint32_t len2 = static_cast<uint32_t>(msg2.size());
  const uint32_t size = len1 + (len2 ? (2 + len2) : 0);  // 2表示':'和' '
  char *result = new char[size + 5];  // state_总长度包括前面5个字节
  memcpy(result, &size, sizeof(size));  // 信息长度存入前4个字节
  result[4] = static_cast<char>(code);  // 状态存入第5个字节
  memcpy(result + 5, msg.data(), len1);  // 从第6个字节开始存储信息内容
  if (len2) {  // 如果msg2不为空,则信息内容加上': ' + msg2
    result[5 + len1] = ':';
    result[6 + len1] = ' ';
    memcpy(result + 7 + len1, msg2.data(), len2);
  }
  state_ = result;
}

判断状态成员方法

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

  // 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; }

  // Returns true iff the status indicates a NotSupportedError.
  bool IsNotSupportedError() const { return code() == kNotSupported; }

  // Returns true iff the status indicates an InvalidArgument.
  bool IsInvalidArgument() const { return code() == kInvalidArgument; }

都是通过调用Code()方法返回状态,Code()实现如下:

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

输出状态

std::string Status::ToString() const {
  if (state_ == nullptr) {
    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));  // 取出state_信息长度存入length
    result.append(state_ + 5, length);  // 添加具体信息
    return result;  // 返回type + 具体信息
  }
}

其中snprintf将格式化的n个字节数据写入字符串,函数原型如下:

 int snprintf(char *str, int n, char * format [, argument, ...]);
posted @ 2020-06-08 08:31  Galaxy_hao  阅读(193)  评论(0编辑  收藏  举报