http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/2f8caedb/weex_core/Source/core/render/page/render_page.cpp ---------------------------------------------------------------------- diff --git a/weex_core/Source/core/render/page/render_page.cpp b/weex_core/Source/core/render/page/render_page.cpp new file mode 100644 index 0000000..c518956 --- /dev/null +++ b/weex_core/Source/core/render/page/render_page.cpp @@ -0,0 +1,631 @@ +#include <core/parser/dom_parser.h> +#include <core/render/action/render_action_add_element.h> +#include <core/render/action/render_action_remove_element.h> +#include <core/render/action/render_action_move_element.h> +#include <core/render/action/render_action_createbody.h> +#include <core/render/action/render_action_update_style.h> +#include <core/render/action/render_action_update_attr.h> +#include <core/render/action/render_action_layout.h> +#include <core/render/action/render_action_createfinish.h> +#include <core/layout/layout.h> +#include <android/base/string/string_utils.h> +#include <core/moniter/render_performance.h> +#include <core/config/core_environment.h> +#include <base/ViewUtils.h> +#include <core/render/action/render_action_add_event.h> +#include <core/render/action/render_action_remove_event.h> +#include <core/css/constants_value.h> +#include "render_page.h" +#include "core/render/manager/render_manager.h" +#include "core/render/node/render_object.h" + +namespace WeexCore { + + static bool splitScreenRendering = false; + + RenderPage::RenderPage(std::string pageId) { + +#if RENDER_LOG + LOGD("[RenderPage] new RenderPage >>>> pageId: %s", pageId.c_str()); +#endif + + mPageId = pageId; + mWXCorePerformance = new RenderPerformance(); + mViewPortWidth = kDefaultViewPortWidth; + renderPageSize.first = WXCoreEnvironment::getInstance()->DeviceWidth(); + renderPageSize.second = NAN; + } + + RenderPage::~RenderPage() { + +#if RENDER_LOG + LOGD("[RenderPage] Delete RenderPage >>>> pageId: %s", mPageId.c_str()); +#endif + + mRenderObjectRegisterMap.clear(); + + if (render_root != nullptr) { + delete render_root; + render_root = nullptr; + } + + if (mWXCorePerformance != nullptr) { + delete mWXCorePerformance; + mWXCorePerformance = nullptr; + } + } + + void RenderPage::CalculateLayout() { + if (render_root == nullptr || !render_root->ViewInit()) + return; + +#if RENDER_LOG + LOGD("[RenderPage] CalculateLayout >>>> pageId: %s", mPageId.c_str()); +#endif + + long long startTime = getCurrentTime(); + render_root->LayoutBefore(); + render_root->calculateLayout(renderPageSize); + render_root->LayoutAfter(); + CssLayoutTime(getCurrentTime() - startTime); + + if (splitScreenRendering) { + if (mAlreadyCreateFinish) { + TraverseTree(render_root, 0); + } else { + float deviceHeight = WXCoreEnvironment::getInstance()->DeviceHeight(); + float deviceWidth = WXCoreEnvironment::getInstance()->DeviceWidth(); + float radio = deviceWidth / (mViewPortWidth * kLayoutFirstScreenOverflowRadio); + + switch (render_root->getFlexDirection()) { + case kFlexDirectionColumn: + case kFlexDirectionColumnReverse: + if (render_root->getLargestMainSize() * radio > deviceHeight / 3) { + TraverseTree(render_root, 0); + } + break; + case kFlexDirectionRow: + case kFlexDirectionRowReverse: + default: + if (render_root->getLargestMainSize() * radio > deviceWidth / 3) { + TraverseTree(render_root, 0); + } + break; + } + } + } else { + TraverseTree(render_root, 0); + } + } + + void RenderPage::TraverseTree(RenderObject *render,int index) { + + if (render == nullptr) + return; + + if (render->hasNewLayout()) { + SendLayoutAction(render, index); + render->setHasNewLayout(false); + } + + for(auto it = render->ChildListIterBegin(); it != render->ChildListIterEnd(); it++) { + RenderObject* child = static_cast<RenderObject*>(*it); + if (child != nullptr) { + TraverseTree(child, it-render->ChildListIterBegin()); + } + } + } + + bool RenderPage::CreateRootRender(RenderObject *root) { + + if (root == nullptr) + return false; + + long long startTime = getCurrentTime(); + SetRootRenderObject(root); + + if (isnan(render_root->getStyleWidth())) { + render_root->setStyleWidthLevel(FALLBACK_STYLE); + if (GetRenderContainerWidthWrapContent()) + render_root->setStyleWidthToNAN(); + else + render_root->setStyleWidth(WXCoreEnvironment::getInstance()->DeviceWidth(), false); + } else { + render_root->setStyleWidthLevel(CSS_STYLE); + } + PushRenderToRegisterMap(root); + + BuildRenderTreeTime(getCurrentTime() - startTime); + SendCreateBodyAction(root); + return true; + } + + void RenderPage::SetRootRenderObject(RenderObject *root) { + if (root != nullptr) { + render_root = root; + render_root->MarkRootRender(); + } + } + + bool RenderPage::AddRenderObject(const std::string &parentRef, int insertPosition, RenderObject *child) { + long long startTime = getCurrentTime(); + RenderObject *parent = GetRenderObject(parentRef); + if (parent == nullptr || child == nullptr) { + return false; + } + + // add child to Render Tree + insertPosition = parent->AddRenderObject(insertPosition, child); + if (insertPosition < -1) { + return false; + } + + PushRenderToRegisterMap(child); + BuildRenderTreeTime(getCurrentTime() - startTime); + SendAddElementAction(child, parent, insertPosition); + + Batch(); + return true; + } + + bool RenderPage::RemoveRenderObject(const std::string &ref) { + long long startTime = getCurrentTime(); + RenderObject *child = GetRenderObject(ref); + if (child == nullptr) + return false; + + RenderObject *parent = child->GetParentRender(); + if (parent == nullptr) + return false; + + parent->RemoveRenderObject(child); + + RemoveRenderFromRegisterMap(child); + delete child; + + BuildRenderTreeTime(getCurrentTime() - startTime); + + SendRemoveElementAction(ref); + return true; + } + + bool RenderPage::MoveRenderObject(const std::string &ref, const std::string &parentRef, int index) { + long long startTime = getCurrentTime(); + + RenderObject *child = GetRenderObject(ref); + if (child == nullptr) + return false; + + RenderObject *oldParent = child->GetParentRender(); + RenderObject *newParent = GetRenderObject(parentRef); + if (oldParent == nullptr || newParent == nullptr) + return false; + + if (oldParent->Ref() == newParent->Ref()) { + if (oldParent->IndexOf(child) < 0) { + return false; + } else if (oldParent->IndexOf(child) == index) { + return false; + } else if (oldParent->IndexOf(child) < index) { + index = index - 1; + } + } + + child->getParent()->removeChild(child); + newParent->addChildAt(child, index); + + BuildRenderTreeTime(getCurrentTime() - startTime); + + SendMoveElementAction(ref, parentRef, index); + return true; + } + + bool RenderPage::UpdateStyle(const std::string &ref, std::vector<std::pair<std::string, std::string>> *src) { + long long startTime = getCurrentTime(); + RenderObject *render = GetRenderObject(ref); + if (render == nullptr || src == nullptr || src->empty()) + return false; + + std::vector<std::pair<std::string, std::string>> *style = nullptr; + std::vector<std::pair<std::string, std::string>> *margin = nullptr; + std::vector<std::pair<std::string, std::string>> *padding = nullptr; + std::vector<std::pair<std::string, std::string>> *border = nullptr; + + bool flag = false; + + int result = Bridge_Impl_Android::getInstance()->callHasTransitionPros(mPageId.c_str(), ref.c_str(), src); + + if (result == 1) { + BuildRenderTreeTime(getCurrentTime() - startTime); + SendUpdateStyleAction(render, src, margin, padding, border); + } else { + for (auto iter = src->begin(); iter != src->end(); iter++) { + switch (render->UpdateStyle((*iter).first, (*iter).second)) { + case kTypeStyle: + if (style == nullptr) { + style = new std::vector<std::pair<std::string, std::string>>(); + } + style->insert(style->end(), (*iter)); + flag = true; + break; + case kTypeMargin: + if (margin == nullptr) { + margin = new std::vector<std::pair<std::string, std::string>>(); + } + render->UpdateStyle((*iter).first, + (*iter).second, + 0, + [=, &flag](float foo) { + (*iter).second = std::to_string(foo), + margin->insert(margin->end(), (*iter)), + flag = true; + }); + break; + case kTypePadding: + if (padding == nullptr) { + padding = new std::vector<std::pair<std::string, std::string>>(); + } + render->UpdateStyle((*iter).first, + (*iter).second, + 0, + [=, &flag](float foo) { + (*iter).second = std::to_string(foo), + padding->insert(padding->end(), (*iter)), + flag = true; + }); + break; + case kTypeBorder: + if (border == nullptr) { + border = new std::vector<std::pair<std::string, std::string>>(); + } + render->UpdateStyle((*iter).first, + (*iter).second, + 0, + [=, &flag](float foo) { + (*iter).second = std::to_string(foo), + border->insert(border->end(), (*iter)), + flag = true; + }); + break; + } + } + } + + BuildRenderTreeTime(getCurrentTime() - startTime); + + if (style != nullptr || margin != nullptr || padding != nullptr || border != nullptr) + SendUpdateStyleAction(render, style, margin, padding, border); + + Batch(); + + if (src != nullptr) { + src->clear(); + src->shrink_to_fit(); + delete src; + src = nullptr; + } + + if (style != nullptr) { + style->clear(); + style->shrink_to_fit(); + delete style; + style = nullptr; + } + + if (margin != nullptr) { + margin->clear(); + margin->shrink_to_fit(); + delete margin; + margin = nullptr; + } + + if (padding != nullptr) { + padding->clear(); + padding->shrink_to_fit(); + delete padding; + padding = nullptr; + } + + if (border != nullptr) { + border->clear(); + border->shrink_to_fit(); + delete border; + border = nullptr; + } + + return flag; + } + + bool RenderPage::UpdateAttr(const std::string &ref, std::vector<std::pair<std::string, std::string>> *attrs) { + long long startTime = getCurrentTime(); + RenderObject *render = GetRenderObject(ref); + if (render == nullptr || attrs == nullptr || attrs->empty()) + return false; + + SendUpdateAttrAction(render, attrs); + + for (auto iter = attrs->cbegin(); iter != attrs->cend(); iter++) { + render->UpdateAttr((*iter).first, (*iter).second); + } + + if (attrs != nullptr) { + attrs->clear(); + attrs->shrink_to_fit(); + delete attrs; + attrs = nullptr; + } + + return true; + } + + void RenderPage::SetDefaultHeightAndWidthIntoRootRender(const float defaultWidth, + const float defaultHeight, + const bool isWidthWrapContent, const bool isHeightWrapContent) { + renderPageSize.first = defaultWidth; + renderPageSize.second = defaultHeight; + if (render_root->getStyleWidthLevel() >= INSTANCE_STYLE) { + render_root->setStyleWidthLevel(INSTANCE_STYLE); + if (isWidthWrapContent) { + SetRenderContainerWidthWrapContent(true); + render_root->setStyleWidthToNAN(); + renderPageSize.first = NAN; + } else { + render_root->setStyleWidth(defaultWidth, true); + } + updateDirty(true); + } + + if (render_root->getStyleHeightLevel() >= INSTANCE_STYLE) { + if(!isHeightWrapContent) { + render_root->setStyleHeightLevel(INSTANCE_STYLE); + render_root->setStyleHeight(defaultHeight); + updateDirty(true); + } + } + + Batch(); + } + + bool RenderPage::AddEvent(const std::string &ref, const std::string &event) { + long long startTime = getCurrentTime(); + RenderObject *render = GetRenderObject(ref); + if (render == nullptr) + return false; + + render->AddEvent(event); + BuildRenderTreeTime(getCurrentTime() - startTime); + + render_action *action = new RenderActionAddEvent(mPageId, ref, event); + PostRenderAction(action); + return true; + } + + bool RenderPage::RemoveEvent(const std::string &ref, const std::string &event) { + long long startTime = getCurrentTime(); + RenderObject *render = GetRenderObject(ref); + if (render == nullptr) + return false; + + render->RemoveEvent(event); + BuildRenderTreeTime(getCurrentTime() - startTime); + + render_action *action = new RenderActionRemoveEvent(mPageId, ref, event); + PostRenderAction(action); + return true; + } + + bool RenderPage::CreateFinish() { + if (render_root == nullptr) { + return false; + } + mAlreadyCreateFinish = true; + Batch(); + SendCreateFinishAction(); + return true; + } + + void RenderPage::LayoutImmediately() { + if(isDirty() && useVSync){ + CalculateLayout(); + needLayout.store(false); + updateDirty(false); + } + } + + void RenderPage::PostRenderAction(render_action *action) { + if (action != nullptr) { + action->ExecuteAction(); + } + } + + void RenderPage::PushRenderToRegisterMap(RenderObject *render) { + if (render == nullptr) + return; + + std::string ref = render->Ref(); + mRenderObjectRegisterMap.insert(std::pair<std::string, RenderObject *>(ref, render)); + + for(auto it = render->ChildListIterBegin(); it != render->ChildListIterEnd(); it++) { + RenderObject* child = static_cast<RenderObject*>(*it); + if (child != nullptr) { + PushRenderToRegisterMap(child); + } + } + } + + void RenderPage::RemoveRenderFromRegisterMap(RenderObject *render) { + if (render == nullptr) + return; + + mRenderObjectRegisterMap.erase(render->Ref()); + + for(auto it = render->ChildListIterBegin(); it != render->ChildListIterEnd(); it++) { + RenderObject* child = static_cast<RenderObject*>(*it); + if (child != nullptr) { + RemoveRenderFromRegisterMap(child); + } + } + } + + void RenderPage::SendCreateBodyAction(RenderObject *render) { + if (render == nullptr) + return; + + render_action *action = new RenderActionCreateBody(PageId(), render); + PostRenderAction(action); + + Index i = 0; + for(auto it = render->ChildListIterBegin(); it != render->ChildListIterEnd(); it++) { + RenderObject* child = static_cast<RenderObject*>(*it); + if (child != nullptr) { + SendAddElementAction(child, render, i); + } + ++i; + } + } + + void RenderPage::SendAddElementAction(RenderObject *child, RenderObject *parent, int index) { + if (child == nullptr || parent == nullptr) + return; + + render_action *action = new RenderActionAddElement(PageId(), child, parent, index); + PostRenderAction(action); + + Index i = 0; + for(auto it = child->ChildListIterBegin(); it != child->ChildListIterEnd(); it++) { + RenderObject* grandson = static_cast<RenderObject*>(*it); + if (grandson != nullptr) { + SendAddElementAction(grandson, child, i); + } + ++i; + } + } + + void RenderPage::SendRemoveElementAction(const std::string &ref) { + render_action *action = new RenderActionRemoveElement(PageId(), ref); + PostRenderAction(action); + } + + void RenderPage::SendMoveElementAction(const std::string &ref, const std::string &parentRef, int index) { + render_action *action = new RenderActionMoveElement(PageId(), ref, parentRef, index); + PostRenderAction(action); + } + + void RenderPage::SendLayoutAction(RenderObject *render, int index) { + if (render == nullptr) + return; + + render_action *action = new RenderActionLayout(PageId(), render, index); + PostRenderAction(action); + } + + void RenderPage::SendUpdateStyleAction(RenderObject *render, + std::vector<std::pair<std::string, std::string>> *style, + std::vector<std::pair<std::string, std::string>> *margin, + std::vector<std::pair<std::string, std::string>> *padding, + std::vector<std::pair<std::string, std::string>> *border) { + render_action *action = new RenderActionUpdateStyle(PageId(), render->Ref(), style, margin, padding, border); + PostRenderAction(action); + } + + void RenderPage::SendUpdateAttrAction(RenderObject *render, + std::vector<std::pair<std::string, std::string>> *attrs) { + render_action *action = new RenderActionUpdateAttr(PageId(), render->Ref(), attrs); + PostRenderAction(action); + } + + void RenderPage::SendCreateFinishAction() { + render_action *action = new RenderActionCreateFinish(PageId()); + PostRenderAction(action); + } + + void RenderPage::JniCallTime(const long long &time) { + if (mWXCorePerformance != nullptr) + mWXCorePerformance->jniCallTime += time; + } + + void RenderPage::CssLayoutTime(const long long &time) { + if (mWXCorePerformance != nullptr) + mWXCorePerformance->cssLayoutTime += time; + } + + void RenderPage::AddEventActionJNITime(const long long &time) { + if (mWXCorePerformance != nullptr) + mWXCorePerformance->addEventActionJNITime += time; + } + + void RenderPage::RemoveEventActionJNITime(const long long &time) { + if (mWXCorePerformance != nullptr) + mWXCorePerformance->removeEventActionJNITime += time; + } + + void RenderPage::AddElementActionJNITime(const long long &time) { + if (mWXCorePerformance != nullptr) + mWXCorePerformance->addElementActionJNITime += time; + } + + void RenderPage::LayoutActionJniTime(const long long &time) { + if (mWXCorePerformance != nullptr) + mWXCorePerformance->layoutActionJniTime += time; + } + + void RenderPage::ParseJsonTime(const long long &time) { + if (mWXCorePerformance != nullptr) + mWXCorePerformance->parseJsonTime += time; + } + + void RenderPage::BuildRenderTreeTime(const long long &time) { + if (mWXCorePerformance != nullptr) + mWXCorePerformance->buildRenderObjectTime += time; + } + + void RenderPage::CreateJMapJNITime(const long long &time) { + if (mWXCorePerformance != nullptr) + mWXCorePerformance->createJMapJNITime += time; + } + + void RenderPage::CallBridgeTime(const long long &time) { + if (mWXCorePerformance != nullptr) + mWXCorePerformance->jniCallBridgeTime += time; + } + + int RenderPage::PrintFirstScreenLog() { + if (mWXCorePerformance != nullptr) + return mWXCorePerformance->PrintPerformanceLog(onFirstScreen); + return 0; + } + + int RenderPage::PrintRenderSuccessLog() { + if (mWXCorePerformance != nullptr) + return mWXCorePerformance->PrintPerformanceLog(onRenderSuccess); + return 0; + } + + void RenderPage::Batch() { + if ((useVSync && needLayout.load()) || !useVSync) { + CalculateLayout(); + needLayout.store(false); + updateDirty(false); + } + } + + void RenderPage::OnRenderPageInit() { + + } + + void RenderPage::OnRenderProcessStart() { + + } + + void RenderPage::OnRenderProcessExited() { + + } + + void RenderPage::OnRenderProcessGone() { + + } + + void RenderPage::OnRenderPageClose() { + + } +} //namespace WeexCore
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/2f8caedb/weex_core/Source/core/render/page/render_page.h ---------------------------------------------------------------------- diff --git a/weex_core/Source/core/render/page/render_page.h b/weex_core/Source/core/render/page/render_page.h new file mode 100644 index 0000000..d5df3ba --- /dev/null +++ b/weex_core/Source/core/render/page/render_page.h @@ -0,0 +1,180 @@ +#ifndef RenderPage_H +#define RenderPage_H + +#include <vector> +#include <string> +#include <map> +#include <jni.h> +#include <cmath> + +namespace WeexCore { + + constexpr float kLayoutFirstScreenOverflowRadio = 1.2f; + + class render_action; + + class RenderObject; + + class RenderPerformance; + + class RenderPage { + + private: + + void TraverseTree(RenderObject *render, int index); + + void PushRenderToRegisterMap(RenderObject *render); + + void RemoveRenderFromRegisterMap(RenderObject *render); + + void SendCreateBodyAction(RenderObject *render); + + void SendAddElementAction(RenderObject *child, RenderObject *parent, int index); + + void SendRemoveElementAction(const std::string &ref); + + void SendMoveElementAction(const std::string &ref, const std::string &parentRef, int index); + + void SendLayoutAction(RenderObject *render, int index); + + void + SendUpdateStyleAction(RenderObject *render, + std::vector<std::pair<std::string, std::string>> *style, + std::vector<std::pair<std::string, std::string>> *margin, + std::vector<std::pair<std::string, std::string>> *padding, + std::vector<std::pair<std::string, std::string>> *border); + + void SendUpdateAttrAction(RenderObject *render, std::vector<std::pair<std::string, std::string>> *attrs); + + void SendCreateFinishAction(); + + void PostRenderAction(render_action *action); + + public: + static constexpr bool useVSync = true; + std::atomic_bool needLayout{false}; + std::atomic_bool hasForeLayoutAction{false}; + RenderPage(std::string pageId); + + ~RenderPage(); + + void CalculateLayout(); + + bool CreateRootRender(RenderObject *root); + + bool AddRenderObject(const std::string &parentRef, int insertPosiotn, RenderObject *child); + + bool RemoveRenderObject(const std::string &ref); + + bool MoveRenderObject(const std::string &ref, const std::string &parentRef, int index); + + bool UpdateStyle(const std::string &ref, std::vector<std::pair<std::string, std::string>> *styles); + + bool UpdateAttr(const std::string &ref, std::vector<std::pair<std::string, std::string>> *attrs); + + void SetDefaultHeightAndWidthIntoRootRender(const float defaultWidth, const float defaultHeight, const bool isWidthWrapContent, const bool isHeightWrapContent); + + bool AddEvent(const std::string &ref, const std::string &event); + + bool RemoveEvent(const std::string &ref, const std::string &event); + + bool CreateFinish(); + + void Batch(); + + void JniCallTime(const long long &time); + + void CssLayoutTime(const long long &time); + + void AddEventActionJNITime(const long long &time); + + void RemoveEventActionJNITime(const long long &time); + + void AddElementActionJNITime(const long long &time); + + void LayoutActionJniTime(const long long &time); + + void ParseJsonTime(const long long &time); + + void BuildRenderTreeTime(const long long &time); + + void CreateJMapJNITime(const long long &time); + + void CallBridgeTime(const long long &time); + + int PrintFirstScreenLog(); + + int PrintRenderSuccessLog(); + + void LayoutImmediately(); + + inline RenderObject *GetRenderObject(const std::string &ref) { + std::map<std::string, RenderObject *>::iterator iter = mRenderObjectRegisterMap.find(ref); + if (iter != mRenderObjectRegisterMap.end()) { + return iter->second; + } else { + return nullptr; + } + } + + void SetRootRenderObject(RenderObject *root); + + inline RenderObject * GetRootRenderObject() const { + return render_root; + } + + inline std::string PageId() { + return mPageId; + } + + inline float ViewPortWidth() const { + return mViewPortWidth; + } + + inline void SetViewPortWidth(float viewPortWidth) { + this->mViewPortWidth = viewPortWidth; + } + + inline bool isDirty(){ + return dirty.load(); + } + + inline void updateDirty(bool dirty){ + this->dirty.store(dirty); + } + + inline void SetRenderContainerWidthWrapContent(bool wrap) { + this->isRenderContainerWidthWrapContent.store(wrap); + } + + inline bool GetRenderContainerWidthWrapContent() { + return isRenderContainerWidthWrapContent.load(); + } + + // ****** Life Cycle ****** // + + void OnRenderPageInit(); + + void OnRenderProcessStart(); + + void OnRenderProcessExited(); + + void OnRenderProcessGone(); + + void OnRenderPageClose(); + + private: + bool mAlreadyCreateFinish = false; + float mViewPortWidth; + RenderObject *render_root = nullptr; + std::string mPageId; + std::pair<float,float> renderPageSize; + std::map<std::string, RenderObject *> mRenderObjectRegisterMap; + RenderPerformance *mWXCorePerformance; + std::atomic_bool dirty{true}; + std::atomic_bool isRenderContainerWidthWrapContent{false}; + std::atomic_bool isRenderContainerHeightWrapContent{false}; + }; +} + +#endif //RenderManager_h \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/2f8caedb/weex_core/Source/rapidjson/allocators.h ---------------------------------------------------------------------- diff --git a/weex_core/Source/rapidjson/allocators.h b/weex_core/Source/rapidjson/allocators.h new file mode 100644 index 0000000..655f4a3 --- /dev/null +++ b/weex_core/Source/rapidjson/allocators.h @@ -0,0 +1,271 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// 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. + +#ifndef RAPIDJSON_ALLOCATORS_H_ +#define RAPIDJSON_ALLOCATORS_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Allocator + +/*! \class rapidjson::Allocator + \brief Concept for allocating, resizing and freeing memory block. + + Note that Malloc() and Realloc() are non-static but Free() is static. + + So if an allocator need to support Free(), it needs to put its pointer in + the header of memory block. + +\code +concept Allocator { + static const bool kNeedFree; //!< Whether this allocator needs to call Free(). + + // Allocate a memory block. + // \param size of the memory block in bytes. + // \returns pointer to the memory block. + void* Malloc(size_t size); + + // Resize a memory block. + // \param originalPtr The pointer to current memory block. Null pointer is permitted. + // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) + // \param newSize the new size in bytes. + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); + + // Free a memory block. + // \param pointer to the memory block. Null pointer is permitted. + static void Free(void *ptr); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// CrtAllocator + +//! C-runtime library allocator. +/*! This class is just wrapper for standard C library memory routines. + \note implements Allocator concept +*/ +class CrtAllocator { +public: + static const bool kNeedFree = true; + void* Malloc(size_t size) { + if (size) // behavior of malloc(0) is implementation defined. + return std::malloc(size); + else + return NULL; // standardize to returning NULL. + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + (void)originalSize; + if (newSize == 0) { + std::free(originalPtr); + return NULL; + } + return std::realloc(originalPtr, newSize); + } + static void Free(void *ptr) { std::free(ptr); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// MemoryPoolAllocator + +//! Default memory allocator used by the parser and DOM. +/*! This allocator allocate memory blocks from pre-allocated memory chunks. + + It does not free memory blocks. And Realloc() only allocate new memory. + + The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. + + User may also supply a buffer as the first chunk. + + If the user-buffer is full then additional chunks are allocated by BaseAllocator. + + The user-buffer is not deallocated by this allocator. + + \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. + \note implements Allocator concept +*/ +template <typename BaseAllocator = CrtAllocator> +class MemoryPoolAllocator { +public: + static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) + + //! Constructor with chunkSize. + /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + } + + //! Constructor with user-supplied buffer. + /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. + + The user buffer will not be deallocated when this allocator is destructed. + + \param buffer User supplied buffer. + \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). + \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) + { + RAPIDJSON_ASSERT(buffer != 0); + RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); + chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer); + chunkHead_->capacity = size - sizeof(ChunkHeader); + chunkHead_->size = 0; + chunkHead_->next = 0; + } + + //! Destructor. + /*! This deallocates all memory chunks, excluding the user-supplied buffer. + */ + ~MemoryPoolAllocator() { + Clear(); + RAPIDJSON_DELETE(ownBaseAllocator_); + } + + //! Deallocates all memory chunks, excluding the user-supplied buffer. + void Clear() { + while (chunkHead_ && chunkHead_ != userBuffer_) { + ChunkHeader* next = chunkHead_->next; + baseAllocator_->Free(chunkHead_); + chunkHead_ = next; + } + if (chunkHead_ && chunkHead_ == userBuffer_) + chunkHead_->size = 0; // Clear user buffer + } + + //! Computes the total capacity of allocated memory chunks. + /*! \return total capacity in bytes. + */ + size_t Capacity() const { + size_t capacity = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + capacity += c->capacity; + return capacity; + } + + //! Computes the memory blocks allocated. + /*! \return total used bytes. + */ + size_t Size() const { + size_t size = 0; + for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) + size += c->size; + return size; + } + + //! Allocates a memory block. (concept Allocator) + void* Malloc(size_t size) { + if (!size) + return NULL; + + size = RAPIDJSON_ALIGN(size); + if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity) + if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) + return NULL; + + void *buffer = reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size; + chunkHead_->size += size; + return buffer; + } + + //! Resizes a memory block (concept Allocator) + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + if (originalPtr == 0) + return Malloc(newSize); + + if (newSize == 0) + return NULL; + + originalSize = RAPIDJSON_ALIGN(originalSize); + newSize = RAPIDJSON_ALIGN(newSize); + + // Do not shrink if new size is smaller than original + if (originalSize >= newSize) + return originalPtr; + + // Simply expand it if it is the last allocation and there is sufficient space + if (originalPtr == reinterpret_cast<char *>(chunkHead_) + RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + chunkHead_->size - originalSize) { + size_t increment = static_cast<size_t>(newSize - originalSize); + if (chunkHead_->size + increment <= chunkHead_->capacity) { + chunkHead_->size += increment; + return originalPtr; + } + } + + // Realloc process: allocate and copy memory, do not free original buffer. + if (void* newBuffer = Malloc(newSize)) { + if (originalSize) + std::memcpy(newBuffer, originalPtr, originalSize); + return newBuffer; + } + else + return NULL; + } + + //! Frees a memory block (concept Allocator) + static void Free(void *ptr) { (void)ptr; } // Do nothing + +private: + //! Copy constructor is not permitted. + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; + //! Copy assignment operator is not permitted. + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; + + //! Creates a new chunk. + /*! \param capacity Capacity of the chunk in bytes. + \return true if success. + */ + bool AddChunk(size_t capacity) { + if (!baseAllocator_) + ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); + if (ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(RAPIDJSON_ALIGN(sizeof(ChunkHeader)) + capacity))) { + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = chunkHead_; + chunkHead_ = chunk; + return true; + } + else + return false; + } + + static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. + + //! Chunk header for perpending to each chunk. + /*! Chunks are stored as a singly linked list. + */ + struct ChunkHeader { + size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). + size_t size; //!< Current size of allocated memory in bytes. + ChunkHeader *next; //!< Next chunk in the linked list. + }; + + ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. + void *userBuffer_; //!< User supplied buffer. + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. + BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ENCODINGS_H_
