mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-30 10:26:12 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			549 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			549 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|    Portions Copyright (c) 2016-Present, Facebook, Inc.
 | |
|    Portions Copyright (c) 2012,2013 Monty Program Ab
 | |
| 
 | |
|    This program is free software; you can redistribute it and/or modify
 | |
|    it under the terms of the GNU General Public License as published by
 | |
|    the Free Software Foundation; version 2 of the License.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|    GNU General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software
 | |
|    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
 | |
| #pragma once
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <string>
 | |
| #include <vector>
 | |
| 
 | |
| #ifdef _WIN32
 | |
| #include <stdlib.h>
 | |
| #define htobe64 _byteswap_uint64
 | |
| #define be64toh _byteswap_uint64
 | |
| #define htobe32 _byteswap_ulong
 | |
| #define be32toh _byteswap_ulong
 | |
| #define htobe16 _byteswap_ushort
 | |
| #define be16toh _byteswap_ushort
 | |
| #endif
 | |
| 
 | |
| #if defined(__APPLE__)
 | |
| #include <libkern/OSByteOrder.h>
 | |
| #define htobe64(x) OSSwapHostToBigInt64(x)
 | |
| #define be64toh(x) OSSwapBigToHostInt64(x)
 | |
| #define htobe32(x) OSSwapHostToBigInt32(x)
 | |
| #define be32toh(x) OSSwapBigToHostInt32(x)
 | |
| #define htobe16(x) OSSwapHostToBigInt16(x)
 | |
| #define be16toh(x) OSSwapBigToHostInt16(x)
 | |
| #endif
 | |
| 
 | |
| /* MySQL header files */
 | |
| #include "./my_global.h"
 | |
| 
 | |
| /* MyRocks header files */
 | |
| #include "./rdb_global.h"
 | |
| #include "./rdb_utils.h"
 | |
| 
 | |
| /* RocksDB header files */
 | |
| #include "rocksdb/slice.h"
 | |
| #include "rocksdb/status.h"
 | |
| 
 | |
| namespace myrocks {
 | |
| 
 | |
| /*
 | |
|   Basic composition functions for a network buffer presented as a MySQL String
 | |
|   ("netstr") which stores data in Network Byte Order (Big Endian).
 | |
| */
 | |
| 
 | |
| inline void rdb_netstr_append_uint64(my_core::String *const out_netstr,
 | |
|                                      const uint64 val) {
 | |
|   DBUG_ASSERT(out_netstr != nullptr);
 | |
| 
 | |
|   // Convert from host machine byte order (usually Little Endian) to network
 | |
|   // byte order (Big Endian).
 | |
|   uint64 net_val = htobe64(val);
 | |
|   out_netstr->append(reinterpret_cast<char *>(&net_val), sizeof(net_val));
 | |
| }
 | |
| 
 | |
| inline void rdb_netstr_append_uint32(my_core::String *const out_netstr,
 | |
|                                      const uint32 val) {
 | |
|   DBUG_ASSERT(out_netstr != nullptr);
 | |
| 
 | |
|   // Convert from host machine byte order (usually Little Endian) to network
 | |
|   // byte order (Big Endian).
 | |
|   uint32 net_val = htobe32(val);
 | |
|   out_netstr->append(reinterpret_cast<char *>(&net_val), sizeof(net_val));
 | |
| }
 | |
| 
 | |
| inline void rdb_netstr_append_uint16(my_core::String *const out_netstr,
 | |
|                                      const uint16 val) {
 | |
|   DBUG_ASSERT(out_netstr != nullptr);
 | |
| 
 | |
|   // Convert from host machine byte order (usually Little Endian) to network
 | |
|   // byte order (Big Endian).
 | |
|   uint16 net_val = htobe16(val);
 | |
|   out_netstr->append(reinterpret_cast<char *>(&net_val), sizeof(net_val));
 | |
| }
 | |
| 
 | |
| /*
 | |
|   Basic network buffer ("netbuf") write helper functions.
 | |
| */
 | |
| 
 | |
| inline void rdb_netbuf_store_uint64(uchar *const dst_netbuf, const uint64 n) {
 | |
|   DBUG_ASSERT(dst_netbuf != nullptr);
 | |
| 
 | |
|   // Convert from host byte order (usually Little Endian) to network byte order
 | |
|   // (Big Endian).
 | |
|   uint64 net_val = htobe64(n);
 | |
|   memcpy(dst_netbuf, &net_val, sizeof(net_val));
 | |
| }
 | |
| 
 | |
| inline void rdb_netbuf_store_uint32(uchar *const dst_netbuf, const uint32 n) {
 | |
|   DBUG_ASSERT(dst_netbuf != nullptr);
 | |
| 
 | |
|   // Convert from host byte order (usually Little Endian) to network byte order
 | |
|   // (Big Endian).
 | |
|   uint32 net_val = htobe32(n);
 | |
|   memcpy(dst_netbuf, &net_val, sizeof(net_val));
 | |
| }
 | |
| 
 | |
| inline void rdb_netbuf_store_uint16(uchar *const dst_netbuf, const uint16 n) {
 | |
|   DBUG_ASSERT(dst_netbuf != nullptr);
 | |
| 
 | |
|   // Convert from host byte order (usually Little Endian) to network byte order
 | |
|   // (Big Endian).
 | |
|   uint16 net_val = htobe16(n);
 | |
|   memcpy(dst_netbuf, &net_val, sizeof(net_val));
 | |
| }
 | |
| 
 | |
| inline void rdb_netbuf_store_byte(uchar *const dst_netbuf, const uchar c) {
 | |
|   DBUG_ASSERT(dst_netbuf != nullptr);
 | |
| 
 | |
|   *dst_netbuf = c;
 | |
| }
 | |
| 
 | |
| inline void rdb_netbuf_store_index(uchar *const dst_netbuf,
 | |
|                                    const uint32 number) {
 | |
|   DBUG_ASSERT(dst_netbuf != nullptr);
 | |
| 
 | |
|   rdb_netbuf_store_uint32(dst_netbuf, number);
 | |
| }
 | |
| 
 | |
| /*
 | |
|   Basic conversion helper functions from network byte order (Big Endian) to host
 | |
|   machine byte order (usually Little Endian).
 | |
| */
 | |
| 
 | |
| inline uint64 rdb_netbuf_to_uint64(const uchar *const netbuf) {
 | |
|   DBUG_ASSERT(netbuf != nullptr);
 | |
| 
 | |
|   uint64 net_val;
 | |
|   memcpy(&net_val, netbuf, sizeof(net_val));
 | |
| 
 | |
|   // Convert from network byte order (Big Endian) to host machine byte order
 | |
|   // (usually Little Endian).
 | |
|   return be64toh(net_val);
 | |
| }
 | |
| 
 | |
| inline uint32 rdb_netbuf_to_uint32(const uchar *const netbuf) {
 | |
|   DBUG_ASSERT(netbuf != nullptr);
 | |
| 
 | |
|   uint32 net_val;
 | |
|   memcpy(&net_val, netbuf, sizeof(net_val));
 | |
| 
 | |
|   // Convert from network byte order (Big Endian) to host machine byte order
 | |
|   // (usually Little Endian).
 | |
|   return be32toh(net_val);
 | |
| }
 | |
| 
 | |
| inline uint16 rdb_netbuf_to_uint16(const uchar *const netbuf) {
 | |
|   DBUG_ASSERT(netbuf != nullptr);
 | |
| 
 | |
|   uint16 net_val;
 | |
|   memcpy(&net_val, netbuf, sizeof(net_val));
 | |
| 
 | |
|   // Convert from network byte order (Big Endian) to host machine byte order
 | |
|   // (usually Little Endian).
 | |
|   return be16toh(net_val);
 | |
| }
 | |
| 
 | |
| inline uchar rdb_netbuf_to_byte(const uchar *const netbuf) {
 | |
|   DBUG_ASSERT(netbuf != nullptr);
 | |
| 
 | |
|   return (uchar)netbuf[0];
 | |
| }
 | |
| 
 | |
| /*
 | |
|   Basic network buffer ("netbuf") read helper functions.
 | |
|   Network buffer stores data in Network Byte Order (Big Endian).
 | |
|   NB: The netbuf is passed as an input/output param, hence after reading,
 | |
|       the netbuf pointer gets advanced to the following byte.
 | |
| */
 | |
| 
 | |
| inline uint64 rdb_netbuf_read_uint64(const uchar **netbuf_ptr) {
 | |
|   DBUG_ASSERT(netbuf_ptr != nullptr);
 | |
| 
 | |
|   // Convert from network byte order (Big Endian) to host machine byte order
 | |
|   // (usually Little Endian).
 | |
|   const uint64 host_val = rdb_netbuf_to_uint64(*netbuf_ptr);
 | |
| 
 | |
|   // Advance pointer.
 | |
|   *netbuf_ptr += sizeof(host_val);
 | |
| 
 | |
|   return host_val;
 | |
| }
 | |
| 
 | |
| inline uint32 rdb_netbuf_read_uint32(const uchar **netbuf_ptr) {
 | |
|   DBUG_ASSERT(netbuf_ptr != nullptr);
 | |
| 
 | |
|   // Convert from network byte order (Big Endian) to host machine byte order
 | |
|   // (usually Little Endian).
 | |
|   const uint32 host_val = rdb_netbuf_to_uint32(*netbuf_ptr);
 | |
| 
 | |
|   // Advance pointer.
 | |
|   *netbuf_ptr += sizeof(host_val);
 | |
| 
 | |
|   return host_val;
 | |
| }
 | |
| 
 | |
| inline uint16 rdb_netbuf_read_uint16(const uchar **netbuf_ptr) {
 | |
|   DBUG_ASSERT(netbuf_ptr != nullptr);
 | |
| 
 | |
|   // Convert from network byte order (Big Endian) to host machine byte order
 | |
|   // (usually Little Endian).
 | |
|   const uint16 host_val = rdb_netbuf_to_uint16(*netbuf_ptr);
 | |
| 
 | |
|   // Advance pointer.
 | |
|   *netbuf_ptr += sizeof(host_val);
 | |
| 
 | |
|   return host_val;
 | |
| }
 | |
| 
 | |
| inline void rdb_netbuf_read_gl_index(const uchar **netbuf_ptr,
 | |
|                                      GL_INDEX_ID *const gl_index_id) {
 | |
|   DBUG_ASSERT(gl_index_id != nullptr);
 | |
|   DBUG_ASSERT(netbuf_ptr != nullptr);
 | |
| 
 | |
|   gl_index_id->cf_id = rdb_netbuf_read_uint32(netbuf_ptr);
 | |
|   gl_index_id->index_id = rdb_netbuf_read_uint32(netbuf_ptr);
 | |
| }
 | |
| 
 | |
| /*
 | |
|   A simple string reader:
 | |
|   - it keeps position within the string that we read from
 | |
|   - it prevents one from reading beyond the end of the string.
 | |
| */
 | |
| 
 | |
| class Rdb_string_reader {
 | |
|   const char *m_ptr;
 | |
|   uint m_len;
 | |
| 
 | |
|  private:
 | |
|   Rdb_string_reader &operator=(const Rdb_string_reader &) = default;
 | |
| 
 | |
|  public:
 | |
|   Rdb_string_reader(const Rdb_string_reader &) = default;
 | |
|   /* named constructor */
 | |
|   static Rdb_string_reader read_or_empty(const rocksdb::Slice *const slice) {
 | |
|     if (!slice) {
 | |
|       return Rdb_string_reader("");
 | |
|     } else {
 | |
|       return Rdb_string_reader(slice);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   explicit Rdb_string_reader(const std::string &str) {
 | |
|     m_len = str.length();
 | |
|     if (m_len) {
 | |
|       m_ptr = &str.at(0);
 | |
|     } else {
 | |
|       /*
 | |
|         One can a create a Rdb_string_reader for reading from an empty string
 | |
|         (although attempts to read anything will fail).
 | |
|         We must not access str.at(0), since len==0, we can set ptr to any
 | |
|         value.
 | |
|       */
 | |
|       m_ptr = nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   explicit Rdb_string_reader(const rocksdb::Slice *const slice) {
 | |
|     m_ptr = slice->data();
 | |
|     m_len = slice->size();
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|     Read the next @param size bytes. Returns pointer to the bytes read, or
 | |
|     nullptr if the remaining string doesn't have that many bytes.
 | |
|   */
 | |
|   const char *read(const uint size) {
 | |
|     const char *res;
 | |
|     if (m_len < size) {
 | |
|       res = nullptr;
 | |
|     } else {
 | |
|       res = m_ptr;
 | |
|       m_ptr += size;
 | |
|       m_len -= size;
 | |
|     }
 | |
|     return res;
 | |
|   }
 | |
| 
 | |
|   bool read_uint8(uint *const res) {
 | |
|     const uchar *p;
 | |
|     if (!(p = reinterpret_cast<const uchar *>(read(1)))) {
 | |
|       return true;  // error
 | |
|     } else {
 | |
|       *res = *p;
 | |
|       return false;  // Ok
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   bool read_uint16(uint *const res) {
 | |
|     const uchar *p;
 | |
|     if (!(p = reinterpret_cast<const uchar *>(read(2)))) {
 | |
|       return true;  // error
 | |
|     } else {
 | |
|       *res = rdb_netbuf_to_uint16(p);
 | |
|       return false;  // Ok
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   bool read_uint64(uint64 *const res) {
 | |
|     const uchar *p;
 | |
|     if (!(p = reinterpret_cast<const uchar *>(read(sizeof(uint64))))) {
 | |
|       return true;  // error
 | |
|     } else {
 | |
|       *res = rdb_netbuf_to_uint64(p);
 | |
|       return false;  // Ok
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   uint remaining_bytes() const { return m_len; }
 | |
| 
 | |
|   /*
 | |
|     Return pointer to data that will be read by next read() call (if there is
 | |
|     nothing left to read, returns pointer to beyond the end of previous read()
 | |
|     call)
 | |
|   */
 | |
|   const char *get_current_ptr() const { return m_ptr; }
 | |
| };
 | |
| 
 | |
| /*
 | |
|   @brief
 | |
|   A buffer one can write the data to.
 | |
| 
 | |
|   @detail
 | |
|   Suggested usage pattern:
 | |
| 
 | |
|     writer->clear();
 | |
|     writer->write_XXX(...);
 | |
|     ...
 | |
|     // Ok, writer->ptr() points to the data written so far,
 | |
|     // and writer->get_current_pos() is the length of the data
 | |
| 
 | |
| */
 | |
| 
 | |
| class Rdb_string_writer {
 | |
|   std::vector<uchar> m_data;
 | |
| 
 | |
|  public:
 | |
|   Rdb_string_writer(const Rdb_string_writer &) = delete;
 | |
|   Rdb_string_writer &operator=(const Rdb_string_writer &) = delete;
 | |
|   Rdb_string_writer() = default;
 | |
| 
 | |
|   void clear() { m_data.clear(); }
 | |
|   void write_uint8(const uint val) {
 | |
|     m_data.push_back(static_cast<uchar>(val));
 | |
|   }
 | |
| 
 | |
|   void write_uint16(const uint val) {
 | |
|     const auto size = m_data.size();
 | |
|     m_data.resize(size + 2);
 | |
|     rdb_netbuf_store_uint16(m_data.data() + size, val);
 | |
|   }
 | |
| 
 | |
|   void write_uint32(const uint val) {
 | |
|     const auto size = m_data.size();
 | |
|     m_data.resize(size + 4);
 | |
|     rdb_netbuf_store_uint32(m_data.data() + size, val);
 | |
|   }
 | |
| 
 | |
|   void write(const uchar *const new_data, const size_t len) {
 | |
|     DBUG_ASSERT(new_data != nullptr);
 | |
|     m_data.insert(m_data.end(), new_data, new_data + len);
 | |
|   }
 | |
| 
 | |
|   uchar *ptr() { return m_data.data(); }
 | |
|   size_t get_current_pos() const { return m_data.size(); }
 | |
| 
 | |
|   void write_uint8_at(const size_t pos, const uint new_val) {
 | |
|     // This function will only overwrite what was written
 | |
|     DBUG_ASSERT(pos < get_current_pos());
 | |
|     m_data.data()[pos] = new_val;
 | |
|   }
 | |
| 
 | |
|   void write_uint16_at(const size_t pos, const uint new_val) {
 | |
|     // This function will only overwrite what was written
 | |
|     DBUG_ASSERT(pos < get_current_pos() && (pos + 1) < get_current_pos());
 | |
|     rdb_netbuf_store_uint16(m_data.data() + pos, new_val);
 | |
|   }
 | |
| 
 | |
|   void truncate(const size_t pos) {
 | |
|     DBUG_ASSERT(pos < m_data.size());
 | |
|     m_data.resize(pos);
 | |
|   }
 | |
| 
 | |
|   void allocate(const size_t len, const uchar val = 0) {
 | |
|     DBUG_ASSERT(len > 0);
 | |
|     m_data.resize(m_data.size() + len, val);
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|     An awful hack to deallocate the buffer without relying on the deconstructor.
 | |
|     This is needed to suppress valgrind errors in rocksdb.partition
 | |
|   */
 | |
|   void free() { std::vector<uchar>().swap(m_data); }
 | |
| };
 | |
| 
 | |
| /*
 | |
|    A helper class for writing bits into Rdb_string_writer.
 | |
| 
 | |
|    The class assumes (but doesn't check) that nobody tries to write
 | |
|    anything to the Rdb_string_writer that it is writing to.
 | |
| */
 | |
| class Rdb_bit_writer {
 | |
|   Rdb_string_writer *m_writer;
 | |
|   uchar m_offset;
 | |
| 
 | |
|  public:
 | |
|   Rdb_bit_writer(const Rdb_bit_writer &) = delete;
 | |
|   Rdb_bit_writer &operator=(const Rdb_bit_writer &) = delete;
 | |
| 
 | |
|   explicit Rdb_bit_writer(Rdb_string_writer *writer_arg)
 | |
|       : m_writer(writer_arg), m_offset(0) {}
 | |
| 
 | |
|   void write(uint size, const uint value) {
 | |
|     DBUG_ASSERT((value & ((1 << size) - 1)) == value);
 | |
| 
 | |
|     while (size > 0) {
 | |
|       if (m_offset == 0) {
 | |
|         m_writer->write_uint8(0);
 | |
|       }
 | |
|       // number of bits to put in this byte
 | |
|       const uint bits = std::min(size, (uint)(8 - m_offset));
 | |
|       uchar *const last_byte =
 | |
|           m_writer->ptr() + m_writer->get_current_pos() - 1;
 | |
|       *last_byte |= (uchar)((value >> (size - bits)) & ((1 << bits) - 1))
 | |
|                     << m_offset;
 | |
|       size -= bits;
 | |
|       m_offset = (m_offset + bits) & 0x7;
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| class Rdb_bit_reader {
 | |
|   const uchar *m_cur;
 | |
|   uchar m_offset;
 | |
|   uint m_ret;
 | |
|   Rdb_string_reader *const m_reader;
 | |
| 
 | |
|  public:
 | |
|   Rdb_bit_reader(const Rdb_bit_reader &) = delete;
 | |
|   Rdb_bit_reader &operator=(const Rdb_bit_reader &) = delete;
 | |
| 
 | |
|   explicit Rdb_bit_reader(Rdb_string_reader *const reader)
 | |
|       : m_cur(nullptr), m_offset(0), m_reader(reader) {}
 | |
| 
 | |
|   // Returns a pointer to an uint containing the bits read. On subsequent
 | |
|   // reads, the value being pointed to will be overwritten.  Returns nullptr
 | |
|   // on failure.
 | |
|   uint *read(uint size) {
 | |
|     m_ret = 0;
 | |
|     DBUG_ASSERT(size <= 32);
 | |
| 
 | |
|     while (size > 0) {
 | |
|       if (m_offset == 0) {
 | |
|         m_cur = (const uchar *)m_reader->read(1);
 | |
|         if (m_cur == nullptr) {
 | |
|           return nullptr;
 | |
|         }
 | |
|       }
 | |
|       // how many bits from the current byte?
 | |
|       const uint bits = std::min((uint)(8 - m_offset), size);
 | |
|       m_ret <<= bits;
 | |
|       m_ret |= (*m_cur >> m_offset) & ((1 << bits) - 1);
 | |
|       size -= bits;
 | |
|       m_offset = (m_offset + bits) & 0x7;
 | |
|     }
 | |
| 
 | |
|     return &m_ret;
 | |
|   }
 | |
| };
 | |
| 
 | |
| template <size_t buf_length>
 | |
| class Rdb_buf_writer {
 | |
|  public:
 | |
|   Rdb_buf_writer(const Rdb_buf_writer &) = delete;
 | |
|   Rdb_buf_writer &operator=(const Rdb_buf_writer &) = delete;
 | |
|   Rdb_buf_writer() { reset(); }
 | |
| 
 | |
|   void write_uint32(const uint32 n) {
 | |
|     DBUG_ASSERT(m_ptr + sizeof(n) <= m_buf.data() + buf_length);
 | |
|     rdb_netbuf_store_uint32(m_ptr, n);
 | |
|     m_ptr += sizeof(n);
 | |
|   }
 | |
| 
 | |
|   void write_uint64(const uint64 n) {
 | |
|     DBUG_ASSERT(m_ptr + sizeof(n) <= m_buf.data() + buf_length);
 | |
|     rdb_netbuf_store_uint64(m_ptr, n);
 | |
|     m_ptr += sizeof(n);
 | |
|   }
 | |
| 
 | |
|   void write_uint16(const uint16 n) {
 | |
|     DBUG_ASSERT(m_ptr + sizeof(n) <= m_buf.data() + buf_length);
 | |
|     rdb_netbuf_store_uint16(m_ptr, n);
 | |
|     m_ptr += sizeof(n);
 | |
|   }
 | |
| 
 | |
|   void write_byte(const uchar c) {
 | |
|     DBUG_ASSERT(m_ptr + sizeof(c) <= m_buf.data() + buf_length);
 | |
|     rdb_netbuf_store_byte(m_ptr, c);
 | |
|     m_ptr += sizeof(c);
 | |
|   }
 | |
| 
 | |
|   void write_index(const uint32 n) { write_uint32(n); }
 | |
| 
 | |
|   void write(const char *buf, const size_t size) {
 | |
|     DBUG_ASSERT(m_ptr + size <= m_buf.data() + buf_length);
 | |
|     memcpy(m_ptr, buf, size);
 | |
|     m_ptr += size;
 | |
|   }
 | |
| 
 | |
|   void write(const uchar *buf, const size_t size) {
 | |
|     DBUG_ASSERT(m_ptr + size <= m_buf.data() + buf_length);
 | |
|     memcpy(m_ptr, buf, size);
 | |
|     m_ptr += size;
 | |
|   }
 | |
| 
 | |
|   void reset() { m_ptr = m_buf.data(); }
 | |
| 
 | |
|   const char *data() const {
 | |
|     return reinterpret_cast<const char *>(m_buf.data());
 | |
|   }
 | |
| 
 | |
|   size_t capacity() { return buf_length; }
 | |
| 
 | |
|   /** Returns actual size of the buffer that has data */
 | |
|   size_t size() { return m_ptr - m_buf.data(); }
 | |
| 
 | |
|   rocksdb::Slice to_slice() { return rocksdb::Slice(data(), size()); }
 | |
| 
 | |
|  private:
 | |
|   std::array<uchar, buf_length> m_buf;
 | |
|   uchar *m_ptr;
 | |
| };
 | |
| 
 | |
| }  // namespace myrocks
 | 
