找回密码
 立即注册
查看: 334|回复: 0

MMKV的原理与实现二

[复制链接]
发表于 2021-11-16 09:13 | 显示全部楼层 |阅读模式
一.MMKV.java


public class MMKV {

  static {

    System.loadLibrary("mmkv");

  }

  private static String rootDir;

  private final long nativeHandle;

  public MMKV(long handle) {

    nativeHandle = handle;

  }

  public static String initialize(Context context) {

    String rootDir = context.getFilesDir().getAbsolutePath() + "/mmkv";

    return initialize(rootDir);

  }

  public static String initialize(String rootDir) {

    MMKV.rootDir = rootDir;

    jniInitialize(MMKV.rootDir);

    return rootDir;

  }

  public static MMKV defaultMMKV() {

    if (rootDir == null) {

      throw new IllegalStateException("You should Call MMKV.initialize() first.");

    }

    long handle = getDefaultMMKV();

    return new MMKV(handle);

  }

  public int getInt(String key, int defaultValue) {

    return getInt(nativeHandle, key, defaultValue);

  }

  public void putInt(String key, int value) {

    putInt(nativeHandle, key, value);

  }

  private static native void jniInitialize(String rootDir);

  private static native long getDefaultMMKV();

  private static native int getInt(long nativeHandle, String key, int defaultValue);

  private static native void putInt(long nativeHandle, String key, int value);

}
二.MainActivity.java



public class MainActivity extends AppCompatActivity {

  private static final String TAG = "MainActivity";

  @Override

  protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

//    MMKV.initialize(this);

    MMKV.initialize("/sdcard/mmkv");

    MMKV mmkv = MMKV.defaultMMKV();

    Log.e(TAG, "a=" + mmkv.getInt("a", 0));

    Log.e(TAG, "b=" + mmkv.getInt("b", 0));

    Log.e(TAG, "c=" + mmkv.getInt("c", 0));

    Log.e(TAG, "d=" + mmkv.getInt("d", 0));

    mmkv.putInt("a", 1);

    mmkv.putInt("b", -1);

    mmkv.putInt("c", 128);

    mmkv.putInt("d", -128);

    Log.e(TAG, "a=" + mmkv.getInt("a", 0));

    Log.e(TAG, "b=" + mmkv.getInt("b", 0));

    Log.e(TAG, "c=" + mmkv.getInt("c", 0));

    Log.e(TAG, "d=" + mmkv.getInt("d", 0));

  }

}

三.MMKV.cpp


#include "MMKV.h"

#include "MMKVLog.h"

#include "PBUtility.h"

#include <string>

#include <sys/stat.h>

#include <fcntl.h>

#include <unistd.h>

#include <sys/mman.h>

static unordered_map<string, MMKV *> *g_instanceDic = 0;

static string g_rootDir;

//默认的mmkv文件

#define DEFAULT_MMAP_ID "mmkv.default"

//系统给我们提供真正的内存时,用页为单位提供

//内存分页大小 一分页的大小

const int DEFAULT_MMAP_SIZE = getpagesize();

const int32_t Fixed32Size = 4;

void MMKV::initializeMMKV(const char *path) {

  g_instanceDic = new unordered_map<string, MMKV *>;

  g_rootDir = path;

  //创建目录

  mkdir(g_rootDir.c_str(), 0777);

}

MMKV *MMKV::defaultMMKV() {

  return mmkvWithID(DEFAULT_MMAP_ID);

}

MMKV *MMKV::mmkvWithID(const string &mmapID) {

  auto itr = g_instanceDic->find(mmapID);

  if (itr != g_instanceDic->end()) {

    MMKV *kv = itr->second;

    return kv;

  }

  //创并放入集合

  auto kv = new MMKV(mmapID);

  (*g_instanceDic)[mmapID] = kv;

  return kv;

}

MMKV::MMKV(const string &mmapID) :m_mmapID(mmapID), m_path(g_rootDir + "/" + mmapID) {

  loadFromFile();

}

MMKV::~MMKV() {

  delete m_output;

  m_output = 0;

  munmap(m_ptr, m_size);

  m_ptr = 0;

  ::close(m_fd);

  auto iter = m_dic.begin();

  while (iter != m_dic.end()) {

    delete iter->second;

    iter = m_dic.erase(iter);

  }

}

//todo 不再使用,请调用close方法(自己编写jni方法,通过jni调用)

void MMKV::close() {

  auto itr = g_instanceDic->find(m_mmapID);

  if (itr != g_instanceDic->end()) {

    g_instanceDic->erase(itr);

  }

  delete this;

}

void MMKV::loadFromFile() {

  m_fd = open(m_path.c_str(), O_RDWR | O_CREAT, S_IRWXU);

  if (m_fd < 0) {

    //打開失敗

    LOGI("打开文件:%s 失败!", m_path.c_str());

  }

  //读取文件大小

  struct stat st = {0};

  if (fstat(m_fd, &st) != -1) {

    m_size = st.st_size;

  }

  LOGI("打开文件:%s [%d]", m_path.c_str(), m_size);

  /**

   * 健壮性。 文件是否已存在,容量是否满足页大小倍数

   */

  m_ptr = static_cast<int8_t *>(mmap(0, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0));

  //文件头4个字节写了数据有效区长度

  memcpy(&m_actualSize, m_ptr, Fixed32Size);

  bool loadFromFile = false;

  //有数据

  if (m_actualSize > 0) {

    //数据长度有效:不能比文件还大

    if (m_actualSize + Fixed32Size <= m_size) {

      loadFromFile = true;

    }

    //其他情况,MMKV是交给用户选择

    // 1、OnErrorDiscard 忽略错误,MMKV会忽略文件中原来的内容

    // 2、OnErrorRecover 还原,MMKV尝试按照自己的方式解析文件,并修正长度

  }

  /**

   * 解析 mmkv 文件中的数据 保存到 map集合中

   */

  if (loadFromFile) {

    // 封装的protobuf解析器

    InputBuffer inputBuffer(m_ptr + Fixed32Size, m_actualSize);

    while (!inputBuffer.isAtEnd()) {

      //

      string key = inputBuffer.readString();

      if (key.length() > 0) {

        //读取value(包含value长度+value数据)

        InputBuffer *value = inputBuffer.readData();

//        unordered_map<string,InputBuffer*>::iterator iter;

        auto iter = m_dic.find(key);

        // 集合中找到了老数据

        if (iter != m_dic.end()){

          //清理老数据

          delete iter->second;

          // java-> map.remove

          m_dic.erase(key);

        }

        //本次数据有效,加入集合

        if (value && value->length() > 0) {

          // java-> map.insert

          m_dic.emplace(key, value);

        }

      }

    }

    //创建输出

    m_output = new OutputBuffer(m_ptr + Fixed32Size + m_actualSize,

                m_size - Fixed32Size - m_actualSize);

  } else{

    //todo 文件有问题,忽略文件已存在的数据

  }

}

int32_t MMKV::getInt(const char *key, int defaultValue) {

  auto itr = m_dic.find(key);

  // 找到了

  if (itr != m_dic.end()) {

    // 获得value-> 解码器

    InputBuffer *buf = itr->second;

    int32_t returnValue = buf->readInt32();

    //多次读取,将position还原为0

    buf->restore();

    return returnValue;

  }

  return defaultValue;

}

void MMKV::putInt(const char *key, int value) {

  size_t size = computeInt32Size(value);

  //编码value

  auto *buffer = new InputBuffer(size);

  // 编码!

  OutputBuffer buf(buffer->data(),buffer->length());

  buf.writeInt32(value);

  //记录到内存

  auto itr = m_dic.find(key);

  if (itr != m_dic.end()) {

    delete itr->second;

  }

  m_dic[key] = buffer;

  //同步到映射区(文件)

  appendDataWithKey(key, buffer);

}

void MMKV::appendDataWithKey(string key, InputBuffer *value) {

  //计算保存这个key-value需要多少字节

  size_t itemSize = computeItemSize(key, value);

  // 空闲空间不够了

  if (itemSize > m_output->spaceLeft()){

    // 计算去重key后的数据 所需的存储空间

    size_t needSize = computeMapSize(m_dic);

    needSize += Fixed32Size; //总长度 4字节

    //小于文件大小

    if (needSize >= m_size){

      int32_t oldSize = m_size;

      do {

        //扩充一倍 为什么??? mmap规则限制:整数倍

        m_size *= 2;

      } while (needSize >= m_size);

      //重新设定文件大小

      ftruncate(m_fd, m_size);

      //解除映射

      munmap(m_ptr, oldSize);

      //重新映射

      m_ptr = (int8_t *) mmap(m_ptr, m_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0);

    }

    //全量更新

    writeAcutalSize(needSize - Fixed32Size);

    delete m_output;

    //创建输出

    m_output = new OutputBuffer(m_ptr + Fixed32Size,

                m_size - Fixed32Size);

    //把map写入文件

    auto iter = m_dic.begin();

    for (; iter != m_dic.end(); iter++) {

      auto k = iter->first;

      auto v = iter->second;

      m_output->writeString(k);

      m_output->writeData(v);

    }

  } else{

    //增量更新

    writeAcutalSize(m_actualSize + itemSize);

    //写入key

    m_output->writeString(key);

    //写入value

    m_output->writeData(value);

  }

}

void MMKV::writeAcutalSize(size_t size) {

  memcpy(m_ptr, &size, Fixed32Size);

  m_actualSize = size;

}
四.InputBuffer.cpp


#include "InputBuffer.h"

InputBuffer::InputBuffer(size_t size) {

  m_size = size;

  m_position = 0;

  isCopy = true;

  if (size > 0) {

    m_buf = static_cast<int8_t *>(malloc(size));

  }

}

InputBuffer::InputBuffer(int8_t *buf, size_t size) {

  m_size = size;

  m_position = 0;

  m_buf = buf;

  isCopy = false;

}

InputBuffer::~InputBuffer() {

  if (isCopy && m_buf){

    free(m_buf);

  }

  m_buf = 0;

}

int8_t InputBuffer::readByte() {

  //不能越界

  if (m_position == m_size) {

    return 0;

  }

  return m_buf[m_position++];

}

std::string InputBuffer::readString() {

  //mmkv保存字符串:字符串长度+字符串

  int32_t size = readInt32();

  //剩下的数据有这么多

  if (size <= (m_size - m_position) && size > 0) {

    std::string result((char *) m_buf + m_position, size);

    m_position += size; //读走了 size个了

    return result;

  }

  return "";

}

int32_t InputBuffer::readInt32() {

  return 0;

}

int32_t InputBuffer::readInt64() {

  //最高位为1

  int8_t i = readByte();

  //i < 0

  // i & 0x80 == 0

  //readByte();

  //readByte();

  // 0

  //取低7位 i& 0x7f

  int tmp = 0;

  // tmp |= newTemp <<7;

  return 0;

}

InputBuffer *InputBuffer::readData() {

  //获得数据长度

  int32_t size = readInt32();

  //有效

  if (size <= m_size - m_position && size > 0) {

    InputBuffer *data = new InputBuffer(m_buf + m_position, size);

    m_position += size;

    return data;

  }

  return 0;

}

void InputBuffer:restore() {

  m_position = 0;

}

float InputBuffer::readFloat() {

  int8_t b1 = readByte();

  int8_t b2 = readByte();

  int8_t b3 = readByte();

  int8_t b4 = readByte();

  int32_t value =

      (((int32_t) b1 & 0xff)) | (((int32_t) b2 & 0xff) << 8) | (((int32_t) b3 & 0xff) << 16) |

      (((int32_t) b4 & 0xff) << 24);

  float returnValue = *(float *) &value;

  return returnValue;

}

五.OutputBuffer.cpp

#include "OutputBuffer.h"

OutputBuffer::OutputBuffer(int8_t *buf, size_t size) {

  m_position = 0;

  m_size = size;

  m_buf = buf;

}

OutputBuffer::~OutputBuffer() {

  m_buf = 0;

}

void OutputBuffer::writeByte(int8_t value) {

  if (m_position == m_size) {

    //满啦,出错啦

    return;

  }

  //将byte放入数组

  m_buf[m_position++] = value;

}

void OutputBuffer::writeInt32(int32_t value) {

  if (value < 0) {

    writeInt64(value);

  } else {

    while (true){

      if (value <= 0x7f){

        writeByte(value);

        break;

      } else{

        // 取低7位,再最高位赋1

        writeByte(value & 0x7f | 0x80);

        value >>= 7;

      }

    }

  }

}

void OutputBuffer::writeInt64(int64_t value) {

  uint64_t i = value;

  while (true){

    if (i & ~0x7f == 0){

      writeByte(i);

      break;

    } else{

      // 取低7位,再最高位赋1

      writeByte(i & 0x7f | 0x80);

      i >>= 7;

    }

  }

}

void OutputBuffer::writeFloat(float value) {

  //float 4字节 转为int32处理

  //共用体或者指针修改

#if 0

  union Converter{

    int32_t first;

    float sencond;

  };

  Converter converter;

  converter.sencond = value;

  int32_t i = converter.first;

#else

  int32_t i = *(int32_t *) &value;

#endif

  //取低8位写入

  writeByte((i) & 0xff);

  writeByte((i >> 8) & 0xff);

  writeByte((i >> 16) & 0xff);

  writeByte((i >> 24) & 0xff);

}

void OutputBuffer::writeString(std::string value) {

  //写入长度

  size_t numberOfBytes = value.size();

  writeInt32(numberOfBytes);

  //写入数据

  memcpy(m_buf + m_position, value.data(), numberOfBytes);

  m_position += numberOfBytes;

}

void OutputBuffer::writeData(InputBuffer *value) {

  //长度

  writeInt32(value->length());

  //数据

  size_t numberOfBytes = value->length();

  memcpy(m_buf + m_position, value->data(), numberOfBytes);

  m_position += numberOfBytes;

}
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2025-5-15 21:06 , Processed in 0.552822 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表