/* * Tencent is pleased to support the open source community by making * MMKV available. * * Copyright (C) 2018 THL A29 Limited, a Tencent company. * All rights reserved. * * Licensed under the BSD 3-Clause License (the "License"); you may not use * this file except in compliance with the License. You may obtain a copy of * the License at * * https://opensource.org/licenses/BSD-3-Clause * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "MiniPBCoder.h" #include "CodedInputData.h" #include "CodedOutputData.h" #include "MMBuffer.h" #include "PBEncodeItem.hpp" #include "PBUtility.h" #include #include #if __has_feature(objc_arc) # error This file must be compiled with MRC. Use -fno-objc-arc flag. #endif using namespace std; namespace mmkv { MiniPBCoder::MiniPBCoder() { m_inputBuffer = nullptr; m_inputData = nullptr; m_outputBuffer = nullptr; m_outputData = nullptr; m_encodeItems = nullptr; } MiniPBCoder::~MiniPBCoder() { delete m_inputData; delete m_outputBuffer; delete m_outputData; delete m_encodeItems; } MiniPBCoder::MiniPBCoder(const MMBuffer *inputBuffer) : MiniPBCoder() { m_inputBuffer = inputBuffer; m_inputData = new CodedInputData(m_inputBuffer->getPtr(), static_cast(m_inputBuffer->length())); } // encode // write object using prepared m_encodeItems[] void MiniPBCoder::writeRootObject() { for (size_t index = 0, total = m_encodeItems->size(); index < total; index++) { PBEncodeItem *encodeItem = &(*m_encodeItems)[index]; switch (encodeItem->type) { case PBEncodeItemType_String: { m_outputData->writeString(*(encodeItem->value.strValue)); break; } case PBEncodeItemType_Data: { m_outputData->writeData(*(encodeItem->value.bufferValue)); break; } case PBEncodeItemType_Container: { m_outputData->writeRawVarint32(encodeItem->valueSize); break; } #ifdef MMKV_IOS_OR_MAC case PBEncodeItemType_NSString: { m_outputData->writeRawVarint32(encodeItem->valueSize); if (encodeItem->valueSize > 0 && encodeItem->value.tmpObjectValue != nullptr) { auto obj = (__bridge NSData *) encodeItem->value.tmpObjectValue; MMBuffer buffer((void *) obj.bytes, obj.length, MMBufferNoCopy); m_outputData->writeRawData(buffer); } break; } case PBEncodeItemType_NSData: { m_outputData->writeRawVarint32(encodeItem->valueSize); if (encodeItem->valueSize > 0 && encodeItem->value.objectValue != nullptr) { auto obj = (__bridge NSData *) encodeItem->value.objectValue; MMBuffer buffer((void *) obj.bytes, obj.length, MMBufferNoCopy); m_outputData->writeRawData(buffer); } break; } case PBEncodeItemType_NSDate: { NSDate *oDate = (__bridge NSDate *) encodeItem->value.objectValue; m_outputData->writeDouble(oDate.timeIntervalSince1970); break; } #endif case PBEncodeItemType_None: { MMKVError("%d", encodeItem->type); break; } } } } size_t MiniPBCoder::prepareObjectForEncode(const string &str) { m_encodeItems->push_back(PBEncodeItem()); PBEncodeItem *encodeItem = &(m_encodeItems->back()); size_t index = m_encodeItems->size() - 1; { encodeItem->type = PBEncodeItemType_String; encodeItem->value.strValue = &str; encodeItem->valueSize = static_cast(str.size()); } encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize; return index; } size_t MiniPBCoder::prepareObjectForEncode(const MMBuffer &buffer) { m_encodeItems->push_back(PBEncodeItem()); PBEncodeItem *encodeItem = &(m_encodeItems->back()); size_t index = m_encodeItems->size() - 1; { encodeItem->type = PBEncodeItemType_Data; encodeItem->value.bufferValue = &buffer; encodeItem->valueSize = static_cast(buffer.length()); } encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize; return index; } size_t MiniPBCoder::prepareObjectForEncode(const vector &v) { m_encodeItems->push_back(PBEncodeItem()); PBEncodeItem *encodeItem = &(m_encodeItems->back()); size_t index = m_encodeItems->size() - 1; { encodeItem->type = PBEncodeItemType_Container; encodeItem->value.strValue = nullptr; for (const auto &str : v) { size_t itemIndex = prepareObjectForEncode(str); if (itemIndex < m_encodeItems->size()) { (*m_encodeItems)[index].valueSize += (*m_encodeItems)[itemIndex].compiledSize; } } encodeItem = &(*m_encodeItems)[index]; } encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize; return index; } size_t MiniPBCoder::prepareObjectForEncode(const MMKVMap &map) { m_encodeItems->push_back(PBEncodeItem()); PBEncodeItem *encodeItem = &(m_encodeItems->back()); size_t index = m_encodeItems->size() - 1; { encodeItem->type = PBEncodeItemType_Container; encodeItem->value.strValue = nullptr; for (const auto &itr : map) { const auto &key = itr.first; const auto &value = itr.second; #ifdef MMKV_IOS_OR_MAC if (key.length <= 0) { #else if (key.length() <= 0) { #endif continue; } size_t keyIndex = prepareObjectForEncode(key); if (keyIndex < m_encodeItems->size()) { size_t valueIndex = prepareObjectForEncode(value); if (valueIndex < m_encodeItems->size()) { (*m_encodeItems)[index].valueSize += (*m_encodeItems)[keyIndex].compiledSize; (*m_encodeItems)[index].valueSize += (*m_encodeItems)[valueIndex].compiledSize; } else { m_encodeItems->pop_back(); // pop key } } } encodeItem = &(*m_encodeItems)[index]; } encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize; return index; } #ifdef MMKV_IOS_OR_MAC size_t MiniPBCoder::prepareObjectForEncode(__unsafe_unretained NSObject *obj) { if (!obj) { return m_encodeItems->size(); } m_encodeItems->push_back(PBEncodeItem()); PBEncodeItem *encodeItem = &(m_encodeItems->back()); size_t index = m_encodeItems->size() - 1; if ([obj isKindOfClass:[NSString class]]) { NSString *str = (NSString *) obj; encodeItem->type = PBEncodeItemType_NSString; NSData *buffer = [[str dataUsingEncoding:NSUTF8StringEncoding] retain]; encodeItem->value.tmpObjectValue = (__bridge void *) buffer; encodeItem->valueSize = static_cast(buffer.length); } else if ([obj isKindOfClass:[NSDate class]]) { NSDate *oDate = (NSDate *) obj; encodeItem->type = PBEncodeItemType_NSDate; encodeItem->value.objectValue = (__bridge void *) oDate; encodeItem->valueSize = pbDoubleSize(oDate.timeIntervalSince1970); encodeItem->compiledSize = encodeItem->valueSize; return index; // double has fixed compilesize } else if ([obj isKindOfClass:[NSData class]]) { NSData *oData = (NSData *) obj; encodeItem->type = PBEncodeItemType_NSData; encodeItem->value.objectValue = (__bridge void *) oData; encodeItem->valueSize = static_cast(oData.length); } else { m_encodeItems->pop_back(); MMKVError("%@ not recognized", NSStringFromClass(obj.class)); return m_encodeItems->size(); } encodeItem->compiledSize = pbRawVarint32Size(encodeItem->valueSize) + encodeItem->valueSize; return index; } MMBuffer MiniPBCoder::getEncodeData(__unsafe_unretained NSObject *obj) { m_encodeItems = new vector(); size_t index = prepareObjectForEncode(obj); PBEncodeItem *oItem = (index < m_encodeItems->size()) ? &(*m_encodeItems)[index] : nullptr; if (oItem && oItem->compiledSize > 0) { m_outputBuffer = new MMBuffer(oItem->compiledSize); m_outputData = new CodedOutputData(m_outputBuffer->getPtr(), m_outputBuffer->length()); writeRootObject(); } return move(*m_outputBuffer); } #endif MMBuffer MiniPBCoder::getEncodeData(const string &str) { m_encodeItems = new vector(); size_t index = prepareObjectForEncode(str); PBEncodeItem *oItem = (index < m_encodeItems->size()) ? &(*m_encodeItems)[index] : nullptr; if (oItem && oItem->compiledSize > 0) { m_outputBuffer = new MMBuffer(oItem->compiledSize); m_outputData = new CodedOutputData(m_outputBuffer->getPtr(), m_outputBuffer->length()); writeRootObject(); } return move(*m_outputBuffer); } MMBuffer MiniPBCoder::getEncodeData(const MMBuffer &buffer) { m_encodeItems = new vector(); size_t index = prepareObjectForEncode(buffer); PBEncodeItem *oItem = (index < m_encodeItems->size()) ? &(*m_encodeItems)[index] : nullptr; if (oItem && oItem->compiledSize > 0) { m_outputBuffer = new MMBuffer(oItem->compiledSize); m_outputData = new CodedOutputData(m_outputBuffer->getPtr(), m_outputBuffer->length()); writeRootObject(); } return move(*m_outputBuffer); } MMBuffer MiniPBCoder::getEncodeData(const vector &v) { m_encodeItems = new vector(); size_t index = prepareObjectForEncode(v); PBEncodeItem *oItem = (index < m_encodeItems->size()) ? &(*m_encodeItems)[index] : nullptr; if (oItem && oItem->compiledSize > 0) { m_outputBuffer = new MMBuffer(oItem->compiledSize); m_outputData = new CodedOutputData(m_outputBuffer->getPtr(), m_outputBuffer->length()); writeRootObject(); } return move(*m_outputBuffer); } MMBuffer MiniPBCoder::getEncodeData(const MMKVMap &map) { m_encodeItems = new vector(); size_t index = prepareObjectForEncode(map); PBEncodeItem *oItem = (index < m_encodeItems->size()) ? &(*m_encodeItems)[index] : nullptr; if (oItem && oItem->compiledSize > 0) { m_outputBuffer = new MMBuffer(oItem->compiledSize); m_outputData = new CodedOutputData(m_outputBuffer->getPtr(), m_outputBuffer->length()); writeRootObject(); } return std::move(*m_outputBuffer); } // decode string MiniPBCoder::decodeOneString() { return m_inputData->readString(); } MMBuffer MiniPBCoder::decodeOneBytes() { return m_inputData->readData(); } vector MiniPBCoder::decodeOneSet() { vector v; auto length = m_inputData->readInt32(); while (!m_inputData->isAtEnd()) { auto value = m_inputData->readString(); v.push_back(move(value)); } return v; } void MiniPBCoder::decodeOneMap(MMKVMap &dic, size_t size, bool greedy) { auto block = [size, this](MMKVMap &dictionary) { if (size == 0) { auto length = m_inputData->readInt32(); } while (!m_inputData->isAtEnd()) { #ifdef MMKV_IOS_OR_MAC const auto &key = m_inputData->readNSString(); if (key.length > 0) { #else const auto &key = m_inputData->readString(); if (key.length() > 0) { #endif auto value = m_inputData->readData(); if (value.length() > 0) { dictionary[key] = move(value); } else { dictionary.erase(key); } } } }; if (greedy) { try { block(dic); } catch (std::exception &exception) { MMKVError("%s", exception.what()); } } else { try { MMKVMap tmpDic; block(tmpDic); dic.swap(tmpDic); } catch (std::exception &exception) { MMKVError("%s", exception.what()); } } } string MiniPBCoder::decodeString(const MMBuffer &oData) { MiniPBCoder oCoder(&oData); return oCoder.decodeOneString(); } MMBuffer MiniPBCoder::decodeBytes(const MMBuffer &oData) { MiniPBCoder oCoder(&oData); return oCoder.decodeOneBytes(); } void MiniPBCoder::decodeMap(MMKVMap &dic, const MMBuffer &oData, size_t size) { MiniPBCoder oCoder(&oData); oCoder.decodeOneMap(dic, size, false); } void MiniPBCoder::greedyDecodeMap(MMKVMap &dic, const MMBuffer &oData, size_t size) { MiniPBCoder oCoder(&oData); oCoder.decodeOneMap(dic, size, true); } vector MiniPBCoder::decodeSet(const MMBuffer &oData) { MiniPBCoder oCoder(&oData); return oCoder.decodeOneSet(); } #ifdef MMKV_IOS_OR_MAC NSObject *MiniPBCoder::decodeObject(const MMBuffer &oData, Class cls) { if (!cls || oData.length() == 0) { return nil; } CodedInputData input(oData.getPtr(), static_cast(oData.length())); if (cls == [NSString class]) { return input.readNSString(); } else if (cls == [NSMutableString class]) { return [NSMutableString stringWithString:input.readNSString()]; } else if (cls == [NSData class]) { return input.readNSData(); } else if (cls == [NSMutableData class]) { return [NSMutableData dataWithData:input.readNSData()]; } else if (cls == [NSDate class]) { return [NSDate dateWithTimeIntervalSince1970:input.readDouble()]; } else { MMKVError("%@ not recognized", NSStringFromClass(cls)); } return nil; } #endif // MMKV_IOS_OR_MAC } // namespace mmkv