【asp.net core 系列】5 布局頁和靜態資源

0. 前言

在之前的4篇的內容里,我們較為詳細的介紹了路由以及控制器還有視圖之間的關係。也就是說,系統如何從用戶的HTTP請求解析到控制器里,然後在控制器里處理數據,並返回給視圖,在視圖中显示出來。這一篇我將為大家介紹基礎的最後一部分,布局頁和靜態資源引入。

1. 布局頁

在控制器和視圖那一篇,我們了解到_ViewStart 里設置了一個Layout屬性的值,這個值正是用來設置布局頁的。所謂的布局頁,就是視圖的公用代碼。在實際開發中,布局頁通常存放我們為整個系統定義的頁面框架,視圖裡寫每個視圖的頁面。

回顧一下,默認的_ViewStart里的內容是:

@{
    Layout = "_Layout";
}

默認的布局頁指定的是名為_Layout的布局頁,在本系列第三篇中,我們得知這個視圖應當在Shared文件夾下,那我們進去看一下這個視圖有什麼內容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - MvcWeb</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">MvcWeb</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2020 - MvcWeb - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @RenderSection("Scripts", required: false)
</body>
</html>

這是默認的布局頁內容,看着挺多的,但是除了一些html代碼,裏面還有一些關鍵的地方需要注意。

1.1 RenderSection

RenderSection 分部渲染,在頁面中創建一個標記,表示這個頁面塊將在子視圖(或者是路由的實際渲染視圖)中添加內容。

來,我們看一下微軟官方給的註釋:

In layout pages, renders the content of the section named name.

意思就是在布局頁中,渲染名稱為name的分部內容。

新創建一個分佈頁,名稱為_Layout1

<html>
    <head>
        <title>Render 測試</title>
    </head>
    <body>
        @RenderSection("SectionDemo")
    </body>
</html>

這個布局頁里什麼都沒有,只有一個RenderSection。現在我們新建一個控制器:

using Microsoft.AspNetCore.Mvc;

namespace MvcWeb.Controllers
{
    public class RenderTestController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
    }
}

創建對應的視圖:

Views / RenderTest/Index.cshtml

先設置布局頁為_Layout1

@{
    Layout = "_Layout1";
}

先試試啟動應用,訪問:

http://localhost:5006/RenderTest/Index

正常情況下,你應該能看到這個頁面:

仔細看一下信息,意思是在 RenderTest/Index.cshtml 視圖中沒有找到 SectionDemo 的分部內容。

那麼,如何在視圖中設置分部內容呢?

@{
    Layout = "_Layout1";
}
@section SectionDemo{
    <h1>你好</h1>

}

使用 @section <Section 名稱> 後面跟一對大括號,在大括號中間的內容就是這個section(分部)的內容。

重啟應用,然後刷新頁面,你能看到這樣的頁面:

如果不做特殊要求的話,定義在布局頁中的分部塊,視圖必須實現。當然,RenderSection還有一個參數,可以用來設置分部不是必須的:

public HtmlString RenderSection(string name, bool required);

1.2 RenderBody

先看下微軟給的官方註釋:

In a Razor layout page, renders the portion of a content page that is not within a named section.

簡單講,如果在布局頁中設置了@RenderBody,那麼在使用了這個布局頁的視圖裡所有沒被分部塊包裹的代碼都會渲染到布局頁中聲明了@RenderBody的地方。

修改_Layout1.cshtml:

<html>
    <head>
        <title>Render 測試</title>
    </head>
    <body>
        <h1>RenderBody 測試 -之前</h1>
        @RenderBody()
        <h1>RenderBody 測試 -之後</h1>
    </body>
</html>

修改RenderTest/Index.cshtml

@{
    Layout = "_Layout1";
}

RenderBody測試
<h1>我是視圖的內容!</h1>

重啟應用,刷新剛剛訪問的頁面:

可以看出,RenderBody渲染的位置。

2. 靜態資源引入

通常情況下,靜態資源的引入與HTML引用js和css等資源是一致的,但是對於我們在編寫系統時自己創建的腳本和樣式表,asp.net core提供了不同的處理方式。那就是服務器端壓縮功能。

asp.net core 3.0 的mvc 默認項目是不啟動這個功能的,需要我們額外的開啟支持。

2.1 開啟支持

先引入 BuildBundleMinifier

cd MvcWeb # 切換目錄到MvcWeb項目下
dotnet add package BuildBundleMinifier

創建 bundleconfig.json

[
    {
        "outputFileName": "wwwroot/css/site.min.css",
        "inputFiles": [
            "wwwroot/css/site.css"
        ]
    },
  	{
        "outputFileName": "wwwroot/js/site.min.js",
        "inputFiles": [
          "wwwroot/js/site.js"
        ],
        "minify": {
          "enabled": true,
          "renameLocals": true
        },
        "sourceMap": false
      }
]

每個節點允許設置項:

  • outputFileName 生成的捆綁壓縮文件,通常路徑攜帶wwwroot
  • inputFiles 數組,包含要壓縮到此次輸出文件的文件路徑,會按照添加的順序依次加入
  • minify 輸出類型的縮小選項,可選。 默認是 enabled: true
  • sourceMap 表示是否為捆綁的文件生成源映射的標記
  • sourceMapRootPath 源映射文件的路徑

2.2 使用

正常情況下在布局頁中,把壓縮后的文件路徑引入即可。不過在開發中,通常按照以下方式引用:

<environment exclude="Development">
    <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment>
<environment include="Development">
    <link rel="stylesheet" href="~/css/site.css" />
</environment>

注: asp-append-version 表示在引用路徑追加一個版本號,這是針對html靜態資源緩存的問題的一個解決方案,這一步是由程序決定的。

environment表示環境,現在大家知道這個寫法就行,在接下來的篇幅會講。

3. 靜態資源目錄

我們知道到目前為止,我們的靜態資源都是在wwwroot目錄下。那麼我們是否可以修改或者添加別的目錄作為靜態資源目錄呢?

在Startup.cs文件內的Configure方法下有這樣一行代碼:

app.UseStaticFiles();

這行代碼的意思就是啟用靜態文件,程序自動從 wwwroot尋找資源。那麼,我們就可以從這個方法入手,設置我們自己的靜態資源:

public static IApplicationBuilder UseStaticFiles(this IApplicationBuilder app, StaticFileOptions options);

我們找到了這個方法的另一個重載版本,裏面有一個參數類:

public class StaticFileOptions : SharedOptionsBase
{
    public StaticFileOptions();
    public StaticFileOptions(SharedOptions sharedOptions);
    public IContentTypeProvider ContentTypeProvider { get; set; }
    public string DefaultContentType { get; set; }
    public HttpsCompressionMode HttpsCompression { get; set; }
    public Action<StaticFileResponseContext> OnPrepareResponse { get; set; }
    public bool ServeUnknownFileTypes { get; set; }
}

並沒有發現我們想要的,先別慌,它還有個父類。我們再去它的父類里看看:

public abstract class SharedOptionsBase
{
    protected SharedOptionsBase(SharedOptions sharedOptions);
    public IFileProvider FileProvider { get; set; }
    public PathString RequestPath { get; set; }
    protected SharedOptions SharedOptions { get; }
}

這下就比較明了了,需要我們提供一個文件提供器,那麼我們來找一個合適的IFileProvider實現類吧:

public class PhysicalFileProvider : IFileProvider, IDisposable

這個類可以滿足我們的要求,它位於命名空間:

namespace Microsoft.Extensions.FileProviders

那麼,添加一組我們自己的配置吧:

using Microsoft.Extensions.FileProviders;

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 省略其他代碼,僅添加以下代碼
    app.UseStaticFiles(new StaticFileOptions
    {
        FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(),"OtherStatic")),
    });
}

在項目的根目錄創建名為OtherStatic的文件夾,然後在裏面創建個文件夾,例如: files,並在這個文件夾里隨便添加一個文件。

然後啟動應用訪問:

http://localhost:5006/files/<你添加的文件(包括後綴名)>

然後能在瀏覽器中看到這個文件被正確響應。

當然,這裏存在一個問題,如果在 OtherStatic中的文件在wwwroot也有相同目錄結構的文件存在,這樣訪問就會出現問題。這時候,可以為我們新加的這個配置設置一個請求前綴:

app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(),"OtherStatic")),
    RequestPath = "/other"
});

重啟程序,然後訪問:

http://localhost:5006/other/files/<你添加的文件(包括後綴名)>

然後就能看到剛才響應的文件,重新訪問之前的路徑,發現瀏覽器提示404。

4. 總結

在這一篇,我們講解了布局頁的內容,靜態資源的壓縮綁定以及添加一個新的靜態資源目錄。通過這幾篇內容,讓我們對asp.net core mvc有了一個基本的認知。下一篇,我們將重新創建一個項目,並結合之前的內容,以實戰為背景,帶領大家完成一個功能完備的web系統。

更多內容煩請關注我的博客《高先生小屋》

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

※別再煩惱如何寫文案,掌握八大原則!

STL常用序列容器

這裏簡要的記述一下STL常用容器的實現原理,要點等內容。

vector

vector是比較常用的stl容器,用法與數組是非類似,其內部實現是連續空間分配,與數組的不同之處在於可彈性增加空間,而array是靜態空間,分配后不能動態擴展。vecotr的實現較為簡單,主要的關鍵點在於當空間不足時,會新分配當前空間2倍的空間,將舊空間數據拷貝到新空間,然後刪除舊空間。

struct _Vector_impl: public _Tp_alloc_type {
    pointer _M_start;   // 元素頭
    pointer _M_finish;  // 元素尾
    pointer _M_end_of_storage;  // 可用空間尾,
          // 省略部分代碼...
};

這個是向尾部添加元素的代碼實現,可以看到如果當前還有剩餘空間的話,直接在尾部添加,如果沒有剩餘空間,則會動態擴展。

void push_back(const value_type& __x) {
    if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage) {
	    _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, __x);
	    ++this->_M_impl._M_finish;
    } else
	  _M_realloc_insert(end(), __x);
}

template<typename _Tp, typename _Alloc>
void vector<_Tp, _Alloc>::_M_realloc_insert(iterator __position, const _Tp& __x) {
    const size_type __len = _M_check_len(size_type(1), "vector::_M_realloc_insert");      // 2倍當前大小
    const size_type __elems_before = __position - begin();
    pointer __new_start(this->_M_allocate(__len));
    pointer __new_finish(__new_start);
    __try {
	  // The order of the three operations is dictated by the C++11
	  // case, where the moves could alter a new element belonging
	  // to the existing vector.  This is an issue only for callers
	  // taking the element by lvalue ref (see last bullet of C++11
	  // [res.on.arguments]).
	  _Alloc_traits::construct(this->_M_impl, __new_start + __elems_before, __x);
	  __new_finish = pointer();

	  __new_finish = std::__uninitialized_move_if_noexcept_a(this->_M_impl._M_start, __position.base(), __new_start, _M_get_Tp_allocator();

	  ++__new_finish;

	  __new_finish = std::__uninitialized_move_if_noexcept_a(__position.base(), this->_M_impl._M_finish, __new_finish, _M_get_Tp_allocator());
	}__catch(...) {
	  if (!__new_finish)
	    _Alloc_traits::destroy(this->_M_impl,
				   __new_start + __elems_before);
	  else
	    std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator());
	  _M_deallocate(__new_start, __len);
	  __throw_exception_again;
	}
      std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator());
      _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start);
      this->_M_impl._M_start = __new_start;
      this->_M_impl._M_finish = __new_finish;
      this->_M_impl._M_end_of_storage = __new_start + __len;
    }

// Called by _M_fill_insert, _M_insert_aux etc.
size_type _M_check_len(size_type __n, const char* __s) const {
    if (max_size() - size() < __n)
	  __throw_length_error(__N(__s));

    const size_type __len = size() + std::max(size(), __n);       // 二倍長
    return (__len < size() || __len > max_size()) ? max_size() : __len;
}

pointer _M_allocate(size_t __n) {
    typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr;
    return __n != 0 ? _Tr::allocate(_M_impl, __n) : pointer();
}

使用時可使用[],因為其已實現重載[]

      reference
      operator[](size_type __n) _GLIBCXX_NOEXCEPT {
	      __glibcxx_requires_subscript(__n);
	      return *(this->_M_impl._M_start + __n);
      }

使用時要注意迭代器失效問題,這個在很多STL容器中都有這個問題。

list

鏈表list,與vector不同,元素在內存中不連續分配,不支持隨機存取,好處就是插入與刪除時間複雜度為O(1)。在STL中,其實現的是雙向鏈表,其節點的定義可以看到有前驅和後繼指針,實現也較為簡單。

  /// An actual node in the %list.
template<typename _Tp>
struct _List_node : public __detail::_List_node_base {
    _Tp _M_data;
    _Tp* _M_valptr() { return std::__addressof(_M_data); }
    _Tp const* _M_valptr() const { return std::__addressof(_M_data); }
};

struct _List_node_base {
    _List_node_base* _M_next;
    _List_node_base* _M_prev;

    static void swap(_List_node_base& __x, _List_node_base& __y) _GLIBCXX_USE_NOEXCEPT;
    void _M_transfer(_List_node_base* const __first, _List_node_base* const __last) _GLIBCXX_USE_NOEXCEPT;
    void _M_reverse() _GLIBCXX_USE_NOEXCEPT;
    void _M_hook(_List_node_base* const __position) _GLIBCXX_USE_NOEXCEPT;
    void _M_unhook() _GLIBCXX_USE_NOEXCEPT;
};

deque

雙端隊列,具體實現不同於vectorlist,它是一小段一小段連續空間,每段連續空間之間通過指針數組(這個數組中存放的是每個連續空間數組的頭指針)串聯起來,這樣就能訪問到所有元素。之所以採用這種存儲布局,是有原因的,是有其應用場景的,等分析完源碼后,我們就明白其為何要這麼做了。

deque源碼分析

我們摘取部分源碼看一下其實現細節。雙端隊列的迭代器實現代碼如下(相較於vectorlist,對元素的訪問因為其存儲布局不同,在每一段連續分配空間的邊緣要做特殊處理):

#define _GLIBCXX_DEQUE_BUF_SIZE 512       // 默認連續空間大小

  _GLIBCXX_CONSTEXPR inline size_t __deque_buf_size(size_t __size) { 
        return (__size < _GLIBCXX_DEQUE_BUF_SIZE ? size_t(_GLIBCXX_DEQUE_BUF_SIZE / __size) :size_t(1)); 
    }
template<typename _Tp, typename _Ref, typename _Ptr>
    struct _Deque_iterator {
      typedef _Deque_iterator<_Tp, _Tp&, _Tp*>	     iterator;
      typedef _Deque_iterator<_Tp, const _Tp&, const _Tp*> const_iterator;
      typedef _Tp*					 _Elt_pointer;
      typedef _Tp**					_Map_pointer;

      static size_t _S_buffer_size() _GLIBCXX_NOEXCEPT { 
            return __deque_buf_size(sizeof(_Tp)); 
      }

      typedef std::random_access_iterator_tag	iterator_category;
      typedef _Tp				value_type;
      typedef _Ptr				pointer;
      typedef _Ref				reference;
      typedef size_t				size_type;
      typedef ptrdiff_t				difference_type;
      typedef _Deque_iterator			_Self;

      _Elt_pointer _M_cur;          // 當前位置
      _Elt_pointer _M_first;        // 每一小段空間的開始
      _Elt_pointer _M_last;         // 每一小段空間的結束
      _Map_pointer _M_node;         // 指針數組,可通過這裏訪問到所有連續存儲空間片段

      // 構造函數
      _Deque_iterator(_Elt_pointer __x, _Map_pointer __y) _GLIBCXX_NOEXCEPT : _M_cur(__x), _M_first(*__y),
_M_last(*__y + _S_buffer_size()), _M_node(__y) { }

      _Deque_iterator() _GLIBCXX_NOEXCEPT: _M_cur(), _M_first(), _M_last(), _M_node() { }

      _Deque_iterator(const iterator& __x) _GLIBCXX_NOEXCEPT: _M_cur(__x._M_cur), _M_first(__x._M_first),
	_M_last(__x._M_last), _M_node(__x._M_node) { }

      iterator _M_const_cast() const _GLIBCXX_NOEXCEPT {
          return iterator(_M_cur, _M_node);     // 返回當前元素迭代器
      }

      reference operator*() const _GLIBCXX_NOEXCEPT { 
          return *_M_cur; 
      }

      pointer operator->() const _GLIBCXX_NOEXCEPT { 
          return _M_cur; 
      }

      // 重載++運算符,可以看到,當_M_cur指向本段連續空間尾部時,訪問下一個元素的話是下一段連續空間的首地址
      _Self& operator++() _GLIBCXX_NOEXCEPT {   
	    ++_M_cur;     
	    if (_M_cur == _M_last) {  
	        _M_set_node(_M_node + 1);   // 移向下一段連續存儲空間
	        _M_cur = _M_first;          // 下一段連續空間的首元素
	    }
	    return *this;
      }

      _Self operator++(int) _GLIBCXX_NOEXCEPT {
	    _Self __tmp = *this;
	    ++*this;
	    return __tmp;
      }

      _Self& operator--() _GLIBCXX_NOEXCEPT {
	    if (_M_cur == _M_first) {             // 與++類似,如果當前是第一個元素,--時,就應該調到上一個連續存儲空間
	        _M_set_node(_M_node - 1);
	        _M_cur = _M_last;           // 移到上一段空間的最後,
	    }
	    --_M_cur;                       // 因為是[start, last)區間,這裏要--_M_cur;
	    return *this;
      }

      _Self operator--(int) _GLIBCXX_NOEXCEPT {
	    _Self __tmp = *this;
	    --*this;
	    return __tmp;
      }

      _Self& operator+=(difference_type __n) _GLIBCXX_NOEXCEPT {
	    const difference_type __offset = __n + (_M_cur - _M_first);
	    if (__offset >= 0 && __offset < difference_type(_S_buffer_size()))  // 如果當前連續空間滿足
	       _M_cur += __n;
	    else {  // 如果當前段連續空間不夠用了,需要計算跳到連續空間
	        const difference_type __node_offset = __offset > 0 ? __offset / difference_type(_S_buffer_size()) : -difference_type((-__offset - 1) / _S_buffer_size()) - 1;
	        _M_set_node(_M_node + __node_offset);
	        _M_cur = _M_first + (__offset - __node_offset * difference_type(_S_buffer_size()));
	    }
	    return *this;
      }

      _Self operator+(difference_type __n) const _GLIBCXX_NOEXCEPT {
	    _Self __tmp = *this;
	    return __tmp += __n;
      }

      _Self& operator-=(difference_type __n) _GLIBCXX_NOEXCEPT {
          return *this += -__n; }

      _Self operator-(difference_type __n) const _GLIBCXX_NOEXCEPT {
	    _Self __tmp = *this;
	    return __tmp -= __n;
      }

      reference operator[](difference_type __n) const _GLIBCXX_NOEXCEPT { return *(*this + __n); }

      // Prepares to traverse new_node.  Sets everything except _M_cur, which should therefore be set by the caller immediately afterwards, based on _M_first and _M_last.
      void _M_set_node(_Map_pointer __new_node) _GLIBCXX_NOEXCEPT {     // 跳到新的一段連續存儲空間
	    _M_node = __new_node;
	    _M_first = *__new_node;
	    _M_last = _M_first + difference_type(_S_buffer_size());
      }
    };

從上面deque迭代器的實現來看,主要需要注意的地方就是每段連續空間的邊緣。看完迭代器后,我們看一下deque類的實現代碼,這裏刪減掉大部分代碼,保留部分代碼。其中重點看一下deque中最常用的push_frontpop_frontpush_backpop_back的實現。push_back時間複雜度O(1)比較好理解,過程類似於vector,但push_front為什麼也是O(1)呢?如果在頭部插入一個元素,第一個連續空間距離起始start還有剩餘空間的的話,直接插入就好了,如果沒有剩餘空間的話,就創建一段新的連續空間,將首地址放到map中,如果map沒有空間放置這個首地址,就調整map,再插入首地址,詳細過程請看源碼的具體實現:

 template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
    class deque : protected _Deque_base<_Tp, _Alloc> {
      typedef _Deque_base<_Tp, _Alloc>			_Base;
      typedef typename _Base::_Tp_alloc_type		_Tp_alloc_type;
      typedef typename _Base::_Alloc_traits		_Alloc_traits;
      typedef typename _Base::_Map_pointer		_Map_pointer;

    public:
      typedef _Tp					value_type;
      typedef typename _Alloc_traits::pointer		pointer;
      typedef typename _Alloc_traits::const_pointer	const_pointer;
      typedef typename _Alloc_traits::reference		reference;
      typedef typename _Alloc_traits::const_reference	const_reference;
      typedef typename _Base::iterator			iterator;
      typedef typename _Base::const_iterator		const_iterator;
      typedef std::reverse_iterator<const_iterator>	const_reverse_iterator;
      typedef std::reverse_iterator<iterator>		reverse_iterator;
      typedef size_t					size_type;
      typedef ptrdiff_t					difference_type;
      typedef _Alloc					allocator_type;

    protected:
      static size_t _S_buffer_size() _GLIBCXX_NOEXCEPT { return __deque_buf_size(sizeof(_Tp)); }

      // Functions controlling memory layout, and nothing else.
      using _Base::_M_initialize_map;
      using _Base::_M_create_nodes;
      using _Base::_M_destroy_nodes;
      using _Base::_M_allocate_node;
      using _Base::_M_deallocate_node;
      using _Base::_M_allocate_map;
      using _Base::_M_deallocate_map;
      using _Base::_M_get_Tp_allocator;

      /**
       *  A total of four data members accumulated down the hierarchy.
       *  May be accessed via _M_impl.*
       */
      using _Base::_M_impl;

    public:
	  // 省略構造函數與析構函數......

      /*
       *  @brief  Assigns a given value to a %deque.
       *  @param  __n  Number of elements to be assigned.
       *  @param  __val  Value to be assigned.
       *
       *  This function fills a %deque with @a n copies of the given
       *  value.  Note that the assignment completely changes the
       *  %deque and that the resulting %deque's size is the same as
       *  the number of elements assigned.
       */
      void assign(size_type __n, const value_type& __val) { _M_fill_assign(__n, __val); }

	  // 省略其他assign重載函數......	


      /// Get a copy of the memory allocation object.
      allocator_type get_allocator() const _GLIBCXX_NOEXCEPT{ return _Base::get_allocator(); }

      // iterators
      /**
       *  Returns a read/write iterator that points to the first element in the
       *  %deque.  Iteration is done in ordinary element order.
       */
      iterator begin() _GLIBCXX_NOEXCEPT { return this->_M_impl._M_start; }
      const_iterator begin() const _GLIBCXX_NOEXCEPT { return this->_M_impl._M_start; }

      /**
       *  Returns a read/write iterator that points one past the last
       *  element in the %deque.  Iteration is done in ordinary
       *  element order.
       */
      iterator end() _GLIBCXX_NOEXCEPT{ return this->_M_impl._M_finish; }
      const_iterator end() const _GLIBCXX_NOEXCEPT { return this->_M_impl._M_finish; }


	// 省略其他迭代器相關代碼......

      // [23.2.1.2] capacity
      /**  Returns the number of elements in the %deque.  */
      size_type size() const _GLIBCXX_NOEXCEPT { return this->_M_impl._M_finish - this->_M_impl._M_start; }

      /**  Returns the size() of the largest possible %deque.  */
      size_type max_size() const _GLIBCXX_NOEXCEPT { return _Alloc_traits::max_size(_M_get_Tp_allocator()); }

      /**
       *  @brief  Resizes the %deque to the specified number of elements.
       *  @param  __new_size  Number of elements the %deque should contain.
       *
       *  This function will %resize the %deque to the specified
       *  number of elements.  If the number is smaller than the
       *  %deque's current size the %deque is truncated, otherwise
       *  default constructed elements are appended.
       */
      void resize(size_type __new_size) {
		  const size_type __len = size();
		  if (__new_size > __len)
	  		  _M_default_append(__new_size - __len);
		  else if (__new_size < __len)
	  		  _M_erase_at_end(this->_M_impl._M_start + difference_type(__new_size));
      }

#if __cplusplus >= 201103L
      /**  A non-binding request to reduce memory use.  */
      void shrink_to_fit() noexcept { _M_shrink_to_fit(); }
#endif

      /**
       *  Returns true if the %deque is empty.  (Thus begin() would
       *  equal end().)
       */
      bool empty() const _GLIBCXX_NOEXCEPT { return this->_M_impl._M_finish == this->_M_impl._M_start; }

      // element access
      /**
       *  @brief Subscript access to the data contained in the %deque.
       *  @param __n The index of the element for which data should be
       *  accessed.
       *  @return  Read/write reference to data.
       *
       *  This operator allows for easy, array-style, data access.
       *  Note that data access with this operator is unchecked and
       *  out_of_range lookups are not defined. (For checked lookups
       *  see at().)
       */
      reference operator[](size_type __n) _GLIBCXX_NOEXCEPT {
		  __glibcxx_requires_subscript(__n);
		  return this->_M_impl._M_start[difference_type(__n)];
      }

    protected:
      /// Safety check used only from at().
      void _M_range_check(size_type __n) const {
		  if (__n >= this->size())
	  		  __throw_out_of_range_fmt(__N("deque::_M_range_check: __n "
				       "(which is %zu)>= this->size() "
				       "(which is %zu)"), __n, this->size());
      }

    public:
      /**
       *  @brief  Provides access to the data contained in the %deque.
       *  @param __n The index of the element for which data should be
       *  accessed.
       *  @return  Read/write reference to data.
       *  @throw  std::out_of_range  If @a __n is an invalid index.
       *
       *  This function provides for safer data access.  The parameter
       *  is first checked that it is in the range of the deque.  The
       *  function throws out_of_range if the check fails.
       */
      reference at(size_type __n) {
		  _M_range_check(__n);
		  return (*this)[__n];
      }

      /**
       *  @brief  Provides access to the data contained in the %deque.
       *  @param __n The index of the element for which data should be
       *  accessed.
       *  @return  Read-only (constant) reference to data.
       *  @throw  std::out_of_range  If @a __n is an invalid index.
       *
       *  This function provides for safer data access.  The parameter is first
       *  checked that it is in the range of the deque.  The function throws
       *  out_of_range if the check fails.
       */
      const_reference at(size_type __n) const {
		  _M_range_check(__n);
		  return (*this)[__n];
      }

      /**
       *  Returns a read/write reference to the data at the first
       *  element of the %deque.
       */
      reference front() _GLIBCXX_NOEXCEPT {
		  __glibcxx_requires_nonempty();
		  return *begin();
      }

      /**
       *  Returns a read/write reference to the data at the last element of the
       *  %deque.
       */
      reference back() _GLIBCXX_NOEXCEPT {
		  __glibcxx_requires_nonempty();
		  iterator __tmp = end();
		  --__tmp;
		  return *__tmp;
      }


      /**
       *  @brief  Add data to the front of the %deque.
       *  @param  __x  Data to be added.
       *
       *  This is a typical stack operation.  The function creates an
       *  element at the front of the %deque and assigns the given
       *  data to it.  Due to the nature of a %deque this operation
       *  can be done in constant time.
       */
      void push_front(const value_type& __x) {		// 如果第一段連續空間頭部還有剩餘空間的話,直接插入元素
		  if (this->_M_impl._M_start._M_cur != this->_M_impl._M_start._M_first) {
	      	_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_start._M_cur - 1, __x);
	      	--this->_M_impl._M_start._M_cur;
	  	  } else		// 如果沒有,在前部重新分配空間
	    	  _M_push_front_aux(__x);
      }

      /**
       *  @brief  Add data to the end of the %deque.
       *  @param  __x  Data to be added.
       *
       *  This is a typical stack operation.  The function creates an
       *  element at the end of the %deque and assigns the given data
       *  to it.  Due to the nature of a %deque this operation can be
       *  done in constant time.
       */
      void push_back(const value_type& __x) {
		  if (this->_M_impl._M_finish._M_cur != this->_M_impl._M_finish._M_last - 1) {
	    	  _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish._M_cur, __x);
	          ++this->_M_impl._M_finish._M_cur;
	  	  } else 
			_M_push_back_aux(__x);
      }

      /**
       *  @brief  Removes first element.
       *
       *  This is a typical stack operation.  It shrinks the %deque by one.
       *
       *  Note that no data is returned, and if the first element's data is
       *  needed, it should be retrieved before pop_front() is called.
       */
      void pop_front() _GLIBCXX_NOEXCEPT {
		  __glibcxx_requires_nonempty();
		  if (this->_M_impl._M_start._M_cur != this->_M_impl._M_start._M_last - 1) {
	    	  _Alloc_traits::destroy(this->_M_impl,	this->_M_impl._M_start._M_cur);
	    	  ++this->_M_impl._M_start._M_cur;
	  	  } else
	  	  _M_pop_front_aux();
      }

      /**
       *  @brief  Removes last element.
       *
       *  This is a typical stack operation.  It shrinks the %deque by one.
       *
       *  Note that no data is returned, and if the last element's data is
       *  needed, it should be retrieved before pop_back() is called.
       */
      void pop_back() _GLIBCXX_NOEXCEPT {
		  __glibcxx_requires_nonempty();
		  if (this->_M_impl._M_finish._M_cur != this->_M_impl._M_finish._M_first) {
	          --this->_M_impl._M_finish._M_cur;
	    	  _Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish._M_cur);
	  	  } else
	  	  _M_pop_back_aux();
      }

      /**
       *  @brief  Inserts given value into %deque before specified iterator.
       *  @param  __position  An iterator into the %deque.
       *  @param  __x  Data to be inserted.
       *  @return  An iterator that points to the inserted data.
       *
       *  This function will insert a copy of the given value before the
       *  specified location.
       */
      iterator insert(iterator __position, const value_type& __x);

      /**
       *  Erases all the elements.  Note that this function only erases the
       *  elements, and that if the elements themselves are pointers, the
       *  pointed-to memory is not touched in any way.  Managing the pointer is
       *  the user's responsibility.
       */
      void clear() _GLIBCXX_NOEXCEPT { _M_erase_at_end(begin()); }

    protected:
      // Internal constructor functions follow.
	  // 省略部分代碼......
	 	
      void _M_push_back_aux(const value_type&);
      void _M_push_front_aux(const value_type&);
      void _M_pop_back_aux();
      void _M_pop_front_aux();

	  // 省略部分代碼......
    };

deque的實現比vectorlist要複雜的多,主要是因為其空間布局不太一樣。下面的代碼主要是對雙端隊列隊首與隊尾的操作(push_frontpush_backpop_frontpop_back)中涉及到空間變動的部分代碼實現:

// Called only if _M_impl._M_finish._M_cur == _M_impl._M_finish._M_last - 1.
template<typename _Tp, typename _Alloc>
void deque<_Tp, _Alloc>::_M_push_back_aux(const value_type& __t) {
	_M_reserve_map_at_back();
	*(this->_M_impl._M_finish._M_node + 1) = this->_M_allocate_node();      // map新指針指向新分配的連續空間
	__try {
	    this->_M_impl.construct(this->_M_impl._M_finish._M_cur, __t);
	    this->_M_impl._M_finish._M_set_node(this->_M_impl._M_finish._M_node + 1);
	    this->_M_impl._M_finish._M_cur = this->_M_impl._M_finish._M_first;
	} __catch(...) {
	    _M_deallocate_node(*(this->_M_impl._M_finish._M_node + 1));
	    __throw_exception_again;
	}
}

// Called only if _M_impl._M_start._M_cur == _M_impl._M_start._M_first.
template<typename _Tp, typename _Alloc>
void deque<_Tp, _Alloc>::_M_push_front_aux(const value_type& __t) {
	_M_reserve_map_at_front();
	*(this->_M_impl._M_start._M_node - 1) = this->_M_allocate_node();       // map指定位置指向新分配的連續空間
	__try {
	    this->_M_impl._M_start._M_set_node(this->_M_impl._M_start._M_node - 1);
	    this->_M_impl._M_start._M_cur = this->_M_impl._M_start._M_last - 1;
	    this->_M_impl.construct(this->_M_impl._M_start._M_cur, __t);
	} __catch(...) {
	    ++this->_M_impl._M_start;
	    _M_deallocate_node(*(this->_M_impl._M_start._M_node - 1));
	    __throw_exception_again;
	}
}

// Called only if _M_impl._M_finish._M_cur == _M_impl._M_finish._M_first.
template <typename _Tp, typename _Alloc>
void deque<_Tp, _Alloc>::_M_pop_back_aux() {
    _M_deallocate_node(this->_M_impl._M_finish._M_first);
    this->_M_impl._M_finish._M_set_node(this->_M_impl._M_finish._M_node - 1);
    this->_M_impl._M_finish._M_cur = this->_M_impl._M_finish._M_last - 1;
    _Alloc_traits::destroy(_M_get_Tp_allocator(), this->_M_impl._M_finish._M_cur);
}

  // Called only if _M_impl._M_start._M_cur == _M_impl._M_start._M_last - 1.
  // Note that if the deque has at least one element (a precondition for this
  // member function), and if
  //   _M_impl._M_start._M_cur == _M_impl._M_start._M_last,
  // then the deque must have at least two nodes.
  template <typename _Tp, typename _Alloc>
void deque<_Tp, _Alloc>::_M_pop_front_aux() {
    _Alloc_traits::destroy(_M_get_Tp_allocator(), this->_M_impl._M_start._M_cur);
    _M_deallocate_node(this->_M_impl._M_start._M_first);
    this->_M_impl._M_start._M_set_node(this->_M_impl._M_start._M_node + 1);
    this->_M_impl._M_start._M_cur = this->_M_impl._M_start._M_first;
}

下面的原代碼是調整map的,如果map沒有適當空間插入新的連續空間首地址,就重新分配map(這種情況比如,map的後面全部插滿了,但前面還大量空着,就需要將目前的map中的元素進行移動,使map的元素分佈在中間位置,首尾兩端是空閑的,以便於後面插入新元素; 如果是map的空間不足了,則需要新分配map空間,新空間大小要大於新指針元素數量+2)。

void _M_reserve_map_at_back(size_type __nodes_to_add = 1) {
    if (__nodes_to_add + 1 > this->_M_impl._M_map_size - (this->_M_impl._M_finish._M_node - this->_M_impl._M_map))
	    _M_reallocate_map(__nodes_to_add, false);
}

void _M_reserve_map_at_front(size_type __nodes_to_add = 1) {
    if (__nodes_to_add > size_type(this->_M_impl._M_start._M_node - this->_M_impl._M_map))
	    _M_reallocate_map(__nodes_to_add, true);
}

template <typename _Tp, typename _Alloc>
void deque<_Tp, _Alloc>::_M_reallocate_map(size_type __nodes_to_add, bool __add_at_front) {
    const size_type __old_num_nodes = this->_M_impl._M_finish._M_node - this->_M_impl._M_start._M_node + 1;
    const size_type __new_num_nodes = __old_num_nodes + __nodes_to_add;

    _Map_pointer __new_nstart;
    if (this->_M_impl._M_map_size > 2 * __new_num_nodes) {
	    __new_nstart = this->_M_impl._M_map + (this->_M_impl._M_map_size - __new_num_nodes) / 2 + (__add_at_front ? __nodes_to_add : 0);	// 這裏新map的開始往後移動了一段位置,是為了將來在前部插入的時候有剩餘空間,後部空餘一段位置也是。
	    if (__new_nstart < this->_M_impl._M_start._M_node)
	    	std::copy(this->_M_impl._M_start._M_node, this->_M_impl._M_finish._M_node + 1, __new_nstart);
	    else
	    	std::copy_backward(this->_M_impl._M_start._M_node, this->_M_impl._M_finish._M_node + 1, __new_nstart + __old_num_nodes);
	} else {
	    size_type __new_map_size = this->_M_impl._M_map_size + std::max(this->_M_impl._M_map_size, __nodes_to_add) + 2;		// 要至少空餘2個空閑位置
	    _Map_pointer __new_map = this->_M_allocate_map(__new_map_size);
	    __new_nstart = __new_map + (__new_map_size - __new_num_nodes) / 2 + (__add_at_front ? __nodes_to_add : 0);
	    std::copy(this->_M_impl._M_start._M_node, this->_M_impl._M_finish._M_node + 1, __new_nstart);
	    _M_deallocate_map(this->_M_impl._M_map, this->_M_impl._M_map_size);

	    this->_M_impl._M_map = __new_map;
	    this->_M_impl._M_map_size = __new_map_size;
	}

    this->_M_impl._M_start._M_set_node(__new_nstart);
    this->_M_impl._M_finish._M_set_node(__new_nstart + __old_num_nodes - 1);
}

更詳細的還是自己看STL的源碼吧,順便吐槽一下STL的源碼,代碼太臃腫了,看起來太累了,如果按照其實現原理,自己實現一個mini版STL,應該會簡潔許多許多。

到這裏,deque中比較核心的源碼已經基本分析完了,也基本展現了deque中幾個關鍵成員函數是如何實現的,其迭代器的實現,其map的實現與調整。

deque與vector、list的對比

vector能夠實現隨機訪問,動態擴展,但在頭部插入O(n),開銷較大,且動態擴展時需要複製所有的元素,同樣效率較低。list插入、刪除頭尾部元素效率很高O(n),但是不能隨機訪問,查找效率O(n),每個節點需要存儲前後節點指針,有較大的額外存儲開銷。而deque等於是在兩種容器的優缺點進行了一定的平衡,在收尾插入、刪除元素,效率很高O(1),在中間插入O(n)都差不多,但其能實現隨機訪問,且動態擴展時不需要複製全體元素,只需要新分配足夠的連續存儲空間,最多重新複製一下map到新map,而map是各個連續存儲空間首地址指針數組,容量相比全體元素小非常多,動態擴展時代價很小。所以,deque相比vector在更一般的情況下有更高的性能,相比list有更小的額外存儲空間(但deque擁有較大的最小內存開銷,原因是它需要map和一段連續存儲空間開銷,即元素數目非常小時開銷比list大,但當元素數目較多時,空間開銷比list少)。

stack

棧也是經常用的數據結構,其實現較為簡單,內部實現依賴deque,當然也可以用vectorlist實現。

// Stack implementation -*- C++ -*-
template<typename _Tp, typename _Sequence = deque<_Tp> >
class stack {
// concept requirements
typedef typename _Sequence::value_type _Sequence_value_type;

public:
    typedef typename _Sequence::value_type		value_type;
    typedef typename _Sequence::reference		reference;
    typedef typename _Sequence::const_reference	const_reference;
    typedef typename _Sequence::size_type		size_type;
    typedef	       _Sequence			container_type;

protected:
    _Sequence c;

public:
	stack(): c() { }
	// 省略構造函數與析構函數......
      /**
       *  Returns true if the %stack is empty.
       */
    bool empty() const { return c.empty(); }

    /**  Returns the number of elements in the %stack.  */
    size_type size() const { return c.size(); }

      /**
       *  Returns a read/write reference to the data at the first
       *  element of the %stack.
       */
    reference top() {
		__glibcxx_requires_nonempty();
		return c.back();
    }
      /**
       *  @brief  Add data to the top of the %stack.
       *  @param  __x  Data to be added.
       *
       *  This is a typical %stack operation.  The function creates an
       *  element at the top of the %stack and assigns the given data
       *  to it.  The time complexity of the operation depends on the
       *  underlying sequence.
       */
    void push(const value_type& __x) { c.push_back(__x); }

      /**
       *  @brief  Removes first element.
       *
       *  This is a typical %stack operation.  It shrinks the %stack
       *  by one.  The time complexity of the operation depends on the
       *  underlying sequence.
       *
       *  Note that no data is returned, and if the first element's
       *  data is needed, it should be retrieved before pop() is
       *  called.
       */
    void pop() {
		__glibcxx_requires_nonempty();
		c.pop_back();
    }

	// 省略其他非關鍵代碼......
}; 

queue

隊列有普通的先進先出的隊列,還有優先隊列,優先級隊列不僅僅要按先後順序,更強調優先級高的先出隊列。

普通隊列的實現

普通隊列的實現與棧實現差不多,也是基於deque實現的。

template<typename _Tp, typename _Sequence = deque<_Tp> >
class queue {
// concept requirements
typedef typename _Sequence::value_type _Sequence_value_type;

public:
    typedef typename	_Sequence::value_type		value_type;
    typedef typename	_Sequence::reference		reference;
    typedef typename	_Sequence::const_reference	const_reference;
    typedef typename	_Sequence::size_type		size_type;
    typedef		_Sequence			container_type;

protected:
      /*  Maintainers wondering why this isn't uglified as per style
       *  guidelines should note that this name is specified in the standard,
       *  C++98 [23.2.3.1].
       *  (Why? Presumably for the same reason that it's protected instead
       *  of private: to allow derivation.  But none of the other
       *  containers allow for derivation.  Odd.)
       */
       ///  @c c is the underlying container.
    _Sequence c;

public:
	queue(): c() { }
	// 省略構造函數與析構函數......

    bool empty() const { return c.empty(); }
    size_type size() const { return c.size(); }

    reference front() {
		__glibcxx_requires_nonempty();
		return c.front();
    }

    reference back() {
		__glibcxx_requires_nonempty();
		return c.back();
    }

    // Add data to the end of the %queue.
    void push(const value_type& __x) { c.push_back(__x); }
      
    // Removes first element.
    void pop() {
		__glibcxx_requires_nonempty();
		c.pop_front();
    }
};

優先隊列priority_queue實現

優先隊列的實現原理是基於堆實現的,堆底層是數組,所以,這裏priority_queue底層的序列容器是vector,選則vector而不是其他容器,是因為優先隊列基於堆,而堆的各種操作中,插入、刪除、都是從尾部插入、刪除操作最後實際上物理刪除的是尾部元素,而且其擴容是2倍擴容,符合二叉樹下一層節點數目是上一次所有數目+1,二倍擴容恰好合適,當然也可以用其他容器(例如deque,但不是最優的)。至於堆實現優先隊列的原理,這裏不再敘述。源碼實現如下:

template<typename _Tp, typename _Sequence = vector<_Tp>, typename _Compare  = less<typename _Sequence::value_type> >
class priority_queue {
#ifdef _GLIBCXX_CONCEPT_CHECKS
// concept requirements
typedef typename _Sequence::value_type _Sequence_value_type;
# if __cplusplus < 201103L
    __glibcxx_class_requires(_Tp, _SGIAssignableConcept)
# endif
    __glibcxx_class_requires(_Sequence, _SequenceConcept)
    __glibcxx_class_requires(_Sequence, _RandomAccessContainerConcept)
    __glibcxx_class_requires2(_Tp, _Sequence_value_type, _SameTypeConcept)
    __glibcxx_class_requires4(_Compare, bool, _Tp, _Tp, _BinaryFunctionConcept)
#endif

#if __cplusplus >= 201103L
template<typename _Alloc>
using _Uses = typename
enable_if<uses_allocator<_Sequence, _Alloc>::value>::type;
#endif

public:
    typedef typename	_Sequence::value_type		value_type;
    typedef typename	_Sequence::reference		 reference;
    typedef typename	_Sequence::const_reference	   const_reference;
    typedef typename	_Sequence::size_type		 size_type;
    typedef		_Sequence			    container_type;
    typedef	       _Compare				    value_compare;

protected:
     _Sequence  c;
    _Compare   comp;	// 優先隊列基於堆,而堆經常需要比較操作

public:
    //    *  @brief  Default constructor creates no elements.
    explicit priority_queue(const _Compare& __x = _Compare(), const _Sequence& __s = _Sequence()): c(__s), comp(__x) { 		
        std::make_heap(c.begin(), c.end(), comp); 	// 構造堆
	}

	// 省略其他構造函數......

      /**
       *  Returns true if the %queue is empty.
       */
    bool empty() const { 
		return c.empty(); 
	}

      /**  Returns the number of elements in the %queue.  */
    size_type size() const { return c.size(); }

      /**
       *  Returns a read-only (constant) reference to the data at the first
       *  element of the %queue.
       */
    const_reference top() const {
		__glibcxx_requires_nonempty();
		return c.front();
    }

      /**
       *  @brief  Add data to the %queue.
       *  @param  __x  Data to be added.
       *
       *  This is a typical %queue operation.
       *  The time complexity of the operation depends on the underlying
       *  sequence.
       */
    void push(const value_type& __x) {		// 優先隊列中插入元素,先放到容器尾部,再進行“上移”操作使之滿足堆性質。
		c.push_back(__x);
		std::push_heap(c.begin(), c.end(), comp);
    }

      /**
       *  @brief  Removes first element.
       *
       *  This is a typical %queue operation.  It shrinks the %queue
       *  by one.  The time complexity of the operation depends on the
       *  underlying sequence.
       *
       *  Note that no data is returned, and if the first element's
       *  data is needed, it should be retrieved before pop() is
       *  called.
       */
    void pop() {		//從優先隊列中彈出首元素
		__glibcxx_requires_nonempty();
		std::pop_heap(c.begin(), c.end(), comp);
		c.pop_back();
    }
};

可以看到只要理解了堆的實現原理,優先隊列的實現原理就非常容易理解,堆的相關STL源碼分析不在這裏繼續分析。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!

過年實在太閒了!實名制展示真實財力 支付寶基金討論區變成「相親聖地」

眾所皆知,中國當地十分流行數位支付,其中又以支付寶、微信支付各佔山頭,幾乎佔據了中國人民的日常生活大小事。而在過年期間,由於中國疫情的關係,不少中國民眾都配合政府宣導的「園地過年」,少去了很多事情,沒想到就有不少人直接在支付寶的基金討論區開始「相親」!

根據《微信上的中國》報導指出,原本支付寶的基金討論區是用來讓網民討論基金、甚至是變相的炫耀自己的投資戰績。但在過年期間,不少支付寶的用戶都將基金討論區變成了相親討論區,不少人在討論區中貼出自己的條件,也曬出自己的財力,而也由於中國奉行的實明制,這些資料、姓名都擁有著極高的真實程度。(但也不能排除有人自己P圖來捏造虛假的收益圖。)

 

其實在中國來講,相親這件事情非常的「直接暴力」,兩造雙方的條件都是赤裸裸的呈現出來,比起好像認識新朋友,更在意的是對方的條件「有多強悍」。所以會選擇用這樣「展現財力」的方式來吸引相親對象,其實某種程度來講,非常符合中國的民情。

 

而也有網友表示,在支付寶的基金討論區相親的話,有幾個好處,首先就是免費,第二就是「實名制」的關係,比起一般網路戀愛來講相對可靠。第三就是基金討論區的客群主要都是在90後的適婚年齡的人。第四就是,會來基金討論區的人,基本上就可以篩選掉月光族與無投資意識的人。

 

網友們對於基金討論區的神奇應用,也讓支付寶官方覺得好笑。支付寶官方在公眾號(類似LINE@或是微信上的「粉絲團」)上發文表示,自己雖然是個數字(數位)生活的開放平台,但沒想想到生活這麼開放。而也有網友對支付寶建議,可以開發相親的小軟體,根據芝麻信用分與資產來進行配對。

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

※別再煩惱如何寫文案,掌握八大原則!

國外測試 macOS Big Sur 使用 Chrome 時,RAM 記憶體使用量是 Safari 的 10 倍以上

Google Chrome 非常吃記憶體這是大家都知道的事情,即便 Chrome 87 版本已經有所改善,但相較於其他瀏覽器,佔用率還是高出不少,最近國外就有人實測 macOS 最新 Big Sur 版本的 Safari vs Chrome 比較,不比還好,一比沒想到竟然相差 10 倍以上,而且是分頁數量越開越多,差距也越來越大,這也代表說 Mac 用戶瀏覽器最好還是使用 Safari。

macOS Big Sur 使用 Chrome 時,RAM 記憶體使用量是 Safari 的 10 倍以上

最近一位 Flotato 的開發者 Morten Just,在官網上分享一篇 “瀏覽器吃掉 RAM” 的文章,他在 macOS 最新系統版本中,實驗兩種情況,來測試 Safari 與 Chrome 佔用多少記憶體。

首先是打開 Twitter.com 網站後,瀏覽一下,然後再開啟 Gmail 電子郵件服務。從下圖可以看到,光是僅開啟兩個網頁分頁,Chrome 最高峰就已經佔用 1GB RAM:

Safari 則保持在 80MB 以下。另外這測試是在 VM 虛擬機上運行

平均來看,Chrome 使用約 730MB,Safari 只有 73MB,差距達到 10 倍:

第二項則是壓力測試,他在 2019 年 MacBook Pro  16 上運行,共打開 54 個網頁分頁,Chrome 斜線快速上升,幾分鐘內 RAM 佔用率就達到了 1500MB:

Safari 依舊非常穩定,從這表格來看低到根本看不出使用多少 RAM:

而換算每個分頁使用的 RAM,Chrome 約 290MB,Safari 只有 12MB,意味著差距來到了 24 倍:

雖然這次實驗主要是為了推廣 Flotato 軟體,但他也提到 Chrome 的 RAM 佔用率比他想像還多,不過他確信 Chrome 還是會有效管理每個分頁佔用 RAM 的情況,進而確保當前網頁瀏覽順暢。

另外當開啟許多應用程式時,如:Sketch、Final Cut、Photoshop,Mac 也會嚴格分配 Chrome 的 RAM 使用率,因此實際使用可能也不會那麼多。

不過可以確定的是,如果你不希望瀏覽器消耗掉你太多 RAM,Safari 絕對是 Mac 系統的最佳選擇。

資料來源:flotato

Chrome 變萌了,教你如何製作小恐龍 QR Code 分享網址

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!

2021 小米元宵節活動 2/22~2/26 開跑!(活動優惠整理)

再過幾天就是元宵節,小米台灣也在今日起推出 2021 小米元宵節活動囉!即日起至 2 月 26 日 23:59 期間,除了不免俗的元宵節現金券優惠,消費者也能透過小米會員積分兌換現金券使用,同時有多款小米人氣明星商品寄出直降優惠、超值組合、加價購等多重好康選擇。如果錯過一月的小米年貨節,不妨把握這次活動入手上次沒買到的小米好物吧!

2021 小米元宵節活動 2/22~2/26 開跑!(活動優惠整理)

上個月才結束的小米年貨節,今(22)日稍早小米台灣又推出新活動啦!稍早宣佈 2021 小米元宵節在即日起至 2 月 26 日推出囉!雖然這次活動限時只有五天,不過消費者除了能在活動期間每日領取元宵節優惠券,還能透過積分獲得額外的現金券,也有像是滿額贈品,另外也有超夯單品再度限量開賣、超值組合優惠、滿額送贈品,及最直接的直降商品優惠!

2021 小米元宵節活動頁面:點我前往

元宵節優惠券

元宵節優惠券分為 NT$100 現金券、 NT$200 現金券 、 NT$600 現金券以及 NT$900 現金券,活動期間每日上午 10 點限量發放,每個小米帳號每天一種面額限領一張。(小提醒:現金券滿指定金額可使用,手機及新品不適用):

頁面領券現金券使用門檻:

NT$100現金券:滿 NT$1000 使用(手機及新品不適用)
NT$200現金券:滿 NT$2000 使用(手機及新品不適用)
NT$600現金券:滿 NT$5500 使用(手機及新品不適用)
NT$900現金券:滿 NT$8500 使用(手機及新品不適用)

積分兌換禮

小米會員可透過分享產品頁面、下載App、註冊會員、收集燃料或消費等方式獲得積分,積分可兌換 NT$50 無門檻現金券、 NT$450 現金券、NT$750 現金券、 NT$1,200 現金券。活動期間每日上午 10 點限量發放:

積分兌換現金券使用門檻:

NT$50現金券:80 積分兌換(手機及新品不適用)
NT$450現金券:120 積分兌換,滿 NT$4,000 使用(手機及新品不適用)
NT$750現金券:100 積分兌換,滿 NT$7,000 使用(手機及新品不適用)
NT$1200現金券:80 積分兌換,滿 NT$10,000 使用(手機及新品不適用)

元宵節超值滿額贈活動

活動期間單筆訂單(不含手機&小米智慧顯示器 65 型)滿指定金額,即可獲贈限量贈品。購買指定商品還可獲得 NT$500 無門檻優惠券:

超值滿額贈贈品內容:

  • 活動1:消費單筆訂單滿 NT$3,000,贈小米手環 4C 或 10000 小米行動電源 3 快充版,單筆訂單只贈送一個;
  • 活動2:消費單筆訂單滿 NT$6,000,贈小米小愛音箱 Play 或 小米藍牙耳機 Earbuds 超值版,單筆訂單只贈送一個;
  • 活動3:消費單筆訂單滿 NT$9,000,贈小米手環 5 或 米家床頭燈 金色,單筆訂單只贈送一個;
  • 活動4:消費單筆訂單滿 NT$12,000,贈小米旅行箱 20吋 或 米家水離子吹風機,單筆訂單只贈送一個;
  • 活動5:消費單筆訂單滿NT$18,000,贈小米小愛音箱 Art 或 小米便攜相片印表機 或 小米真無線藍牙耳機 2S,單筆訂單只贈送一個;訂單金額滿NT$18000時,活動 1~4  需四選一
  • 小提醒:上述消費單筆訂單滿額規則不含手機、小米智慧顯示器 65 型。贈品數量有限,贈完為止。)

購物返券活動內容:

單筆訂單若包含「米家掃拖機器人/米家掃地機器人/米家無線吸塵器 G10/米家空氣淨化器 Pro/米家掃拖機器人 1C/米家掃拖機器人 G1/米家手持無線吸塵器 Lite/90分金屬旅行箱」,即可獲贈「NT$500 無門檻優惠券」,優惠券須於發放後 30 天內使用,手機及新品不適用。

超夯單品開賣

小米智慧顯示器 65 型再次於今(22)日 10:00 限量開賣,訂單需再下訂一小時內完成付款。由於預計發貨時間為 3 月上旬,建議將小米智慧顯示器 65 型單獨下單。

另外,上個月預告即將開賣的 Redmi 9T 也在今(22)日起正式開賣。 Redmi 9T(4GB+64GB)提供碳纖灰、暮光藍、日出橙和海洋綠四種顏色,售價為 NT$4,699 ;Redmi 9T(6GB+128GB)提供暮光藍、日出橙和海洋綠三種顏色,售價為 NT$5,499 :

超值組合

凡於小米元宵節活動期間購買以下商品套餐組合,即可享套餐超值優惠價:

超值組套餐內容:

  1. 小米液晶手寫板 2個裝,組合價:NT$840
  2. 小米木紋滑鼠墊+小米無線雙模滑鼠 靜音版,組合價:NT$680
  3. 米家聲波電動牙刷頭 敏感型 +米家聲波電動牙刷頭 通用型,任意兩件組合價:NT$610
  4. 米家電動刮鬍刀 S500+刀頭,組合價:NT$995
  5. 米家無線吸塵器G10/G9 HEPA 濾芯套裝+米家無線吸塵器mini HEPA濾芯(兩個裝)+米家無線吸塵器G10拖地附件套裝,任意兩件組合價:NT$360
  6. 小米便攜印表機即貼相紙+小米便攜相片印表機,組合價:NT$1,750
  7. 米家自動感應洗手機 單機版+小衛質品泡沫洗手液(三瓶裝) ,組合價:NT$460
  8. 米家吸頂燈 450+Yeelight 遙控器,組合價:NT$1790;
  9. 貝醫生巴氏牙刷 四色裝 兩套,組合價:NT$330;
  10. 米家便攜電動刮鬍刀 1S(往復雙刀頭)+刀頭,組合價:NT$1,100
  11. 小米智慧攝影機 雲台版 2K Pro 兩個裝,組合價:NT$2,090
  12. 單筆訂單,購買表內任意商品,滿2件減NT$80,滿3件及以上減 NT$150
    米家掃拖機器人G1 邊刷 / 米家掃拖機器人G1 集塵盒濾網 / 米家掃拖機器人G1 一次性拖布 / 米家掃拖機器人G1 拖布 / 米家掃拖機器人G1 主刷罩 / 米家掃拖機器人G1 主刷 / 米家掃拖機器人 1C 塵盒濾網 白色 / 米家掃拖機器人 1C 主刷罩 白色 / 米家掃拖機器人1C 邊刷 白色 / 米家掃拖機器人 1C 主刷 橘色 / 米家掃拖機器人 1C 防潮墊 白色 / 米家掃拖機器人 1C 拖布 灰色 / 小米Type-C to Lightning 傳輸線(1m) / 小米 USB 3.0 HUB / 小米 Micro USB 傳輸線 編織線版 100cm 紅色 / 小米 Micro USB 傳輸線 編織線版 100cm 黑色 / 小米 USB-C 傳輸線 編織線版 100cm 紅色 / 小米 USB-C 傳輸線 編織線版 100cm 黑色 / 小米 USB Type-c to Type-c 傳輸線 150cm / 小米二合一傳輸線 30cm / 小米二合一傳輸線 100cm / 米家掃地機器人主刷 橘色 / 米家掃地機器人邊刷 / 米家掃地機器人塵盒濾網 / 米家掃地機器人主刷罩 / 米家掃地機器人虛擬牆 棕色 / 小米迷你雙肩包 芭比粉 / 小米迷你雙肩包 檸檬黃 / 小米迷你雙肩包 松柏綠 /

元宵節加價購

活動期間購買指定商品,即可享優惠價加價購入相關配件:

加價購活動內容:

  • 買 家無線吸塵器 G10 加 NT$140 購米家無線吸塵器G10/G9 HEPA 濾芯套裝 或 米家無線吸塵器G10拖地附件套裝 一個;
  • 買米家無線吸塵器 G10 加 NT$1495 購米家無線吸塵器G10/G9 增程電池一個
  • 買小米手環 5 加 NT$10 購腕帶一條或充電線 一個,同一筆訂單購買兩個手環可以加購兩個相同顏色的腕帶,如需不同顏色,可以分開下單
  • 買小米手環 4C 加 NT$10 購腕帶一條 ,同一筆訂單購買兩個手環可以加購兩個相同顏色的腕帶,如需不同顏色,可以分開下單;
  • 買米家無線吸塵器 mini 加NT$95 購米家無線吸塵器mini HEPA濾芯(兩個裝)
  • 買「指定商品」 加 NT$795 購小米手環 5 一個;加 NT$395 購小米手環 4C 一個
    指定商品包含:
    小米真無線藍牙耳機 2S /小米小愛音箱 Art /小米頭戴式藍牙耳機 /20000 小米行動電源 3 高配版 /小米 50W 雙模行動電源 1A1C /10000 小米行動電源 QC3.0 高配版 灰色 /10000 小米行動電源 QC3.0 高配版 金色 /小米圈鐵耳機 Pro /20000 小米行動電源 3 快充版 /小米小愛音箱 Play /10000 小米行動電源 3 無線版 超值版 黑色 /10000 小米行動電源 3 無線版 超值版 白色 /小米藍牙耳機 Earbuds 超值版 /10000 小米行動電源 3 快充版 銀色 /10000 小米行動電源 3 快充版 黑色 /小米雙單元半入耳式耳機 / Redmi行動電源 10000 標準版 黑色 /Redmi行動電源 10000 標準版 白色 /5000 小米行動電源 2
  • 小提醒:加購產品數量有限,售完為止。

元宵節商品直降活動

小米元宵節活動期間,凡參與優惠活動的商品在活動頁面上會顯示「省xxx」標籤,消費者可在小米年貨節活動期間點擊「立即購買」標籤查看參與活動的相關商品型號,參與直降活動的商品內容與幅度依照台灣小米公佈為準。

小米元宵節直降商品清單(完整商品內容依照小米官方公佈為準):

和過去的活動一樣,小米活動頁面的商品並未按照商品類型排列,為方便各位更容易閱讀,筆者也將小米年貨節活動頁面的商品清單重新整理了優惠商品清單如下:

  • 米家掃拖機器人:NT$8,495(省 NT$500)
  • 米家掃地機器人:NT$7,695(省 NT$200)
  • 米家手持無線吸塵器 Lite:NT$4,495(省 NT$500)
  • 小米多功能網關 2:NT$575(省 NT$20)
  • 小米無線網關:NT$175(省 NT$20)
  • 小米光照度感應器:NT$235(省 NT$10)
  • 米家智慧插座 ZigBee:NT$445(省 NT$50)
  • 米家 IH 電子鍋:NT$2,395(省 NT$100)
  • 米家電磁爐:NT$1,595(省 NT$200)
  • 知吾煮湯鍋 米家定製:NT$585(省 NT$10)
  • 米家恆溫電水壺:NT$1,095(省 NT$100)
  • 小米小愛音箱 Art:NT$1,295(省 NT$200)
  • 小米戶外藍牙喇叭:NT$845(省 NT$50)
  • 小米真無線藍牙耳機 2S:NT$1,795(省 NT$200)
  • 小米藍牙耳機 Earbuds 超值版:NT$505(省 NT$40)
  • 小米便攜相片印表機:NT$1,495(省 NT$170)
  • 米家電動打氣機:NT$945(省 NT$50)
  • 米家電動起子機:NT$795(省 NT$100)
  • 小米旅行箱 20 吋(灰色):NT$1,495(省 NT$100)
  • 小米極簡都市雙肩包 2(深灰/藍色):NT$685(省 NT$80)
  • 小米簡約休閒雙肩包(黑色/灰色):NT$445(省 NT$50)
  • 米家電動刮鬍刀 S500:NT$895(省 NT$100)
  • 小米手環 5:NT$845(省 NT$100)
  • 小米手環 4C:NT$445(省 NT$50)
  • 小米液晶手寫板:NT$445(省 NT$50)
  • 小米藍芽自拍桿(黑色/灰色)
  • 20000 小米行動電源3 高配版:NT$945(省 NT$50)
  • Redmi 行動電源 10000 標準版:NT$295(省 NT$50)
  • 5000 小米行動電源 2:NT$255(省 NT$10)
  • 小米 50W 雙模行動電源 1A1C:NT$745(省 NT$50)
  • 小米GaN充電器 Type-C 65W:NT$685(省 NT$80)
  • 小米車用充電器(快充版 37W):NT$245(省 NT$20)

2021 小米元宵節活動頁面:點我前往

圖片/消息來源:小米台灣

延伸閱讀:
Redmi K40 系列「雙旗艦」官方文宣釋出:確認搭載三鏡頭主相機, 2/25 晚間登場

小米11 非官方拆解影片來了!有更多官方未提到的設計小巧思

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

※別再煩惱如何寫文案,掌握八大原則!

Tesla Model 3 與 Model Y 雙版本價位探新低,但又無預警砍了最便宜的版本…

最近 Tesla 的一波價格操作,應該搞得不少汽車品牌心中七上八下。首先,他們宣布現階段相對入門的車款 Model 3 與 Model Y 的 SR+ 後驅低階版的售價,變成最便宜僅需約新台幣 103 萬即可入手。然而當不少人可能還在猶豫要不要入手的次週,這些車款竟隨著 LR 也降價 1,000 美元的消息,一刀就被 Tesla 給砍了。還對下訂 SR+ 版本的消費者發出訊息,表示會將庫存僅會提供給已經結清的車主… 繼續閱讀 Tesla Model 3 與 Model Y 雙版本價位探新低,但又無預警砍了最便宜的版本… 報導內文。

Tesla Model 3 與 Model Y 雙版本價位探新低,但又無預警砍了最便宜的版本…

其實像是這樣看得到買不到的狀況,在 Tesla 已經不是第一次見到了 — Model 3 的 3.5 萬美元 SR 就是門市限定且無預警被砍。現在,Tesla 這樣曇花一現提供牌價最低近 3.7 萬美元的 Model 3 SR+ / Model Y SR+ 版,更是只賣了一週的時間就被消失。

雖然 Tesla 並沒有解釋為何整個品牌現階段最低價的轎車與休旅車款為何「又」被砍。但有人認為,這應該是因為 Elon Musk 先前提到,認為電動車續航應該至少要有 400 公里 / 250 英里的發言有關。而對應這樣的車款精簡策略,Tesla 則是選擇將 Model 3 與 Model Y 的 Long Range 長續航版本降價 1,000 美元,最低,約 130 萬台幣即可入手全輪驅動的 Model 3 LR。

不過儘管這續航可能達不到要求,但對於消費者而言能有更低價入手電動車的選擇,實在是不太能理解有哪裡不好的。也許就當成這是 Tesla 對於低價車款的出清,再來看看調整為僅剩 LR 與性能版的 Model 3 / Model Y 是否會再蹦出什麼有趣(最好是很便宜)的級距囉 — 還是,這是為說好的 70 萬台幣超入門車款鋪路?

是說,台灣這邊的 Model 3 SR+ 倒是完全沒動靜啊…

本篇不是特斯拉廣編,只是因為阿達沒有終身超充,如果您使用阿達的連結 (推薦碼:koc59401)訂車的話,你可以得到1500公里免費超充,我也可以得到1500公里超充(直接網路訂就什麼都沒有)。如果您已經訂車還沒領車的話,還是可以請業務補登記獲得1500公里超充,別讓自己的權力睡著了,等你交車之後也可以用自己的連結或推薦碼找親友購車,如果覺得本篇資訊有幫到你的話,就賞臉一下吧。

TESLA Model 3 訂車連結(請點我)
TESLA 車主討論社團(請點我)

延伸閱讀:

酷寒導致大停電,德州一家四口靠 Tesla 電動車取暖撐過夜晚

AirPods 3 新洩漏照流出,可選配耳塞加強「主動式降噪」體驗?

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!

手機回收率僅5.2%,環保署擬強制回收,二手機怎麼辦!?

環保署表示台灣手機回收率僅五.二%,沒有妥善回收的廢棄手機,恐嚴重污染環境,擬將手機納入公告應強制回收項目。
手機若被環保署納入應強制回收項目,以後舊手機就要強制被回收嗎? 台灣舊手機回收率就會因此提高嗎? 以下作一解析:

掌握最新電信資費訊息,請加入小丰子3C俱樂部粉絲頁!

小丰子3C俱樂部

 

1.台灣目前手機回收率:

環保署統計,國人每年購買約六百萬支手機,但台灣手機回收率僅五.二%,沒有妥善回收的廢棄手機,恐嚴重污染環境。環保署擬將手機納入公告應強制回收項目。
環保署表示,據二○一九年清潔隊統計,整年回收約卅八.一萬支手機,若以手機平均使用汰換壽命為兩年推算,二○一七年手機銷售量七三三萬支,可得出二○一九年回收率僅五.二%。而手機回收率低,是普遍現象,如中國二○一八年手機回收率不到二%,德國僅四%。手機回收率低主要是許多人換新機時,舊機會留著備用,有些人會賣回給業者作為二手機,加上不少民眾擔心個資外洩問題,寧願把舊手機留下來。
由於手機有許多貴金屬,每一千支手機能提煉出價值八萬元的貴金屬,二萬二千支手機就可提煉出一公斤黃金。舊手機若進入回收體系,有望成「城市礦山」,不採礦也能獲得貴重金屬。若舊手機處理不當,手機中的鉛、鉻、汞等有害物質會污染土壤和地下水,嚴重威脅人體健康。

 

2.手機列入應回收項目會發生什麼事?

依據廢棄物清理法第 15 條第 2 項規定,環保署公告應回收廢棄物項目分為容器與物品兩大類,細分 13 類 33 項。

 

目前環保署公告列管資訊類項目有八項,包括可攜式電腦、顯示器、主機板、外接式硬碟、電源器、機殼、印表機、鍵盤。手機則是依廢棄物清理法,可由清潔隊回收處理。

 

依據廢棄物清理法第16條的規定,經環保署公告指定應回收的物品及容器,由其製造及輸入業者負擔回收清除處理的責任。責任業者除了要向環保署辦理登記外,於每期營業稅申報繳納後十五日內,依照公告的費率,繳納回收清除處理費。環保署表示若正式將手機納入公告應回收項目,生產者要依照環保署公告的費率,繳納回收清除處理費,作為回收管理基金,維持後端回收體系穩定運作。若參考歐盟做法將手機比照電子產品一同處理,一支手機回收費率約廿二至三十元。

換言之,手機若正式被納入公告應回收項目,並不是強制手機用戶必須將不用舊手機要強制回收,而是未來在台灣銷售手機廠商必須繳納每台$23~$30的回收清除費用給環保署以充實回收管理基金。對手機用戶而言,頂多就是環保署會更密集舉辦廢手機回收抽獎活動或是增加超商回收折抵金額(現行$12元調高到$20元以上)。

 

因舊手機回收的金額不高與二手機的行情差異很大,加上台灣現行就手機回收率在國際水準來看並不差,版主認為很難吸引用戶提升舊手機回收意願,倒是環保署的回收管理基金將可望每年增加1.5億元以上的回收管理基金成了最大贏家,手機廠商對於天外飛來的環保規費只能當作功德了,呵呵 !

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

※別再煩惱如何寫文案,掌握八大原則!

國外多位用戶反應 M1 Mac 的 SSD 損耗太嚴重,擔心影響使用壽命

M1 Mac 自從推出以來就好評不斷,特別是在效能部分,不過隨著時間拉長,最近也陸續傳出一些問題,繼先前的粉紅小方塊、導致 iPhone 與 iPad Apps 閃退狀況,近日又有一個新問題出現,而且似乎是所有 Mac 都受影響。國外用戶反應,M1 Mac 的 SSD 耗損有點嚴重,健康度令人擔憂。

國外多位用戶反應 M1 Mac 的 SSD 損耗太嚴重

根據外媒 Macworld 報導,最近多位用戶陸續在 Twitter 上分享擔心 M1 Mac SSD 耗損速度的貼文,首先是 Longhorn,他提到他的 M1 Mac 只使用約 2 個月左右,SSD 卻已經損耗 1%,而且這塊 SSD 是焊死的不能更換,讓人覺得非常不 OK:

M1 Mac SSD update…

Apple, this is NOT okay. Especially as the internal SSD isn’t replaceable. The machine is just a bit over 2 months old. pic.twitter.com/xqlSMjxwTa

— Longhorn (@never_released) February 13, 2021

Macworld 編輯 Dan Moren 還跟他的舊 Mac 比較。左側為他舊的 2017 iMac,右側則是去年秋天買的 M1 Air,才三個多月過去,M1 Air 讀寫數據量已經是 3.5 年 iMac 的 15~20%:

This story about M1 Macs with excessive SSD read/write data does seem to be true. On the left, my 2017 iMac; on the right, my M1 Air from last fall. Somehow, in 3 months, it’s registered about 15-20% of what my iMac has in 3.5 years? Not sure what the implications are, but weird. pic.twitter.com/qNrnkzo62z

— Dan Moren (@dmoren) February 23, 2021

這位 David 的 M1 Macbook Pro 配備 2TB SSD 超大容量,才經過 2 個月,SSD 就已經損耗 3%。有人甚至推測,如果他的 M1 MBP 是 256GB 版本,現在搞不好 SSD 已經損耗 30%,並且在兩年後達到最高的 TBW 限制:

16GB M1 MBP, 2TB SSD, 2 months in. pic.twitter.com/SaSmieaT1s

— David (@david_rysk) February 15, 2021

即便 TBW 達標,SSD 依舊還是可以繼續使用,只是不確定可以維持多長時間。

目前還不確定是什麼導致這原因,但應該不是 M1 晶片,畢竟是檢測報告,實際是不是真的這樣,也只能等有人達到 TPW 限制才能得知,另外也可能是測試工具有問題。

無論如何,以 Apple 這麼大間且知名的公司來說,開發時應該有考量到這部分,M1 Mac 用戶也不用太過於擔心。

資料來源:Macworld

國外 YouTuber 實測 M1 MacBook Air 貼上導熱矽膠片後,效能有感提升(更接近 MacBook Pro)

您也許會喜歡:

【推爆】終身$0月租 打電話只要1元/分

立達合法徵信社-讓您安心的選擇

【其他文章推薦】

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!

不可不知的JSON處理庫(cJSON)

ANSI C中的超輕量級JSON解析器

JSON(JavaScript對象表示法)是一種輕量級的數據交換格式。人類易於閱讀和書寫。機器很容易解析和生成。它基於JavaScript編程語言標準ECMA-262第三版(1999年12月)的子集 。JSON是一種完全獨立於語言的文本格式,但是使用C語言家族(包括C,C ++,C#,Java,JavaScript,Perl,Python等)的程序員熟悉的約定。這些屬性使JSON成為理想的數據交換語言。

cJSON旨在成為您可以完成工作的最簡單的解析器。它是資源只有一個C的頭文件和C文件,所以方便移植。它可以為你各種需要的json字符串處理,包括打包、解析、修改、刪除、添加等。在這裏將一探究竟。

在這裏將着重敘述json的打包和解析,更多處理玩法,見文章末尾鏈接。

開始cJSON

cJSON合併到您的項目

因為整個庫只有一個C文件和一個頭文件,所以您只需複製cJSON.h並複製cJSON.c到項目源並開始使用它。

cJSON用ANSI C(C89)編寫,以便支持盡可能多的平台和編譯器。

 

下載:

https://github.com/DaveGamble/cJSON/releases

 

Cjson結構體

/* The cJSON structure: */
typedef struct cJSON
{
    struct cJSON *next;
    struct cJSON *prev;
    struct cJSON *child;
    int type;
    char *valuestring;
    int valueint;
    double valuedouble;
    char *string;
} cJSON;

結構體項解析:

next 和prev :Cjson結構體作為一個雙向連表的環,可以通過 next 和prev 指針進行連表遍歷

child:可以是cJSON_Array、cJSON_Object類型數據

type:當前項的類型

valuestring:內容存儲,當類型是cJSON_String和cJSON_Raw

valueint:內容存儲,整型,可以是cJSON_False、cJSON_True數據

valuedouble:內容存儲,浮點型,當類型是cJSON_Number

string:鍵名

數據類型

l  cJSON_Invalid表示一個不包含任何值的無效項目。如果將項目設置為全零字節,則將自動具有此類型。

l  cJSON_False表示一個false布爾值。您也可以使用來檢查布爾值cJSON_IsBool

l  cJSON_True表示一個true布爾值。您也可以使用來檢查布爾值cJSON_IsBool

l  cJSON_NULL表示一個null值

l  cJSON_Number 表示一個数字值。該值存儲為double in valuedouble和in valueint。如果数字超出整數範圍,INT_MAX或INT_MIN用於valueint

l  cJSON_String表示一個字符串值。它以零終止字符串的形式存儲在中valuestring

l  cJSON_Array表示一個數組值。這是通過指向表示數組中值child的cJSON項目的鏈接列表來實現的。使用next和將元素鏈接在一起prev,其中第一個元素具有prev.next == NULL和最後一個元素next == NULL

l  cJSON_Object 表示一個對象值。對象的存儲方式與數組相同,唯一的區別是對象中的項將其鍵存儲在中string

l  cJSON_Raw表示以JSON字符存儲的零終止形式的任何JSON valuestring。例如,可以使用它來避免一遍又一遍地打印相同的靜態JSON以節省性能。解析時,cJSON永遠不會創建此類型。另請注意,cJSON不會檢查其是否為有效JSON。

類型

#define cJSON_Invalid (0)
#define cJSON_False  (1 << 0)
#define cJSON_True   (1 << 1)
#define cJSON_NULL   (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array  (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw    (1 << 7) /* raw json */

類型判斷

cJSON_IsInvalid(const cJSON * const item);
cJSON_IsFalse(const cJSON * const item);
cJSON_IsTrue(const cJSON * const item);
cJSON_IsBool(const cJSON * const item);
cJSON_IsNull(const cJSON * const item);
cJSON_IsNumber(const cJSON * const item);
cJSON_IsString(const cJSON * const item);
cJSON_IsArray(const cJSON * const item);
cJSON_IsObject(const cJSON * const item);
cJSON_IsRaw(const cJSON * const item);

注意

創建cjson對象后,處理完需要進行內存釋放:

如果是cjson里的對象,請使用cJSON_Delete()

如果不是對象:cJSON_free()或free()

 

零字符

cJSON不支持包含零字符’\0’或的字符串\u0000。對於當前的API,這是不可能的,因為字符串以零結尾。

 

字符編碼

cJSON僅支持UTF-8編碼的輸入。但是在大多數情況下,它不會拒絕無效的UTF-8作為輸入,而只是將其原樣傳播。只要輸入不包含無效的UTF-8,輸出將始終是有效的UTF-8。

 

C標準

cJSON用ANSI C(或C89,C90)編寫。如果您的編譯器或C庫未遵循此標準,則不能保證正確的行為。

 

注意:ANSI C不是C ++,因此不應使用C ++編譯器進行編譯。您可以使用C編譯器對其進行編譯,然後將其與C ++代碼鏈接。儘管可以使用C ++編譯器進行編譯,但是不能保證正確的行為。

 

浮點数字

double除IEEE754雙精度浮點數外,cJSON不正式支持任何實現。它可能仍然可以與其他實現一起使用,但是這些實現的錯誤將被視為無效。

 

目前,cJSON支持的浮點文字的最大長度為63個字符。

 

數組和對象的深層嵌套

cJSON不支持嵌套太深的數組和對象,因為這會導致堆棧溢出。為了防止這種CJSON_NESTING_LIMIT情況,默認情況下,cJSON將深度限製為1000,但是可以在編譯時進行更改。

格式化輸出

按標準的格式輸出json字符串,輸出完后一定要記得釋放內存

 代碼

 1 #include <stdio.h>
 2 #include "cJSON.h"
 3 #include "cJSON.c"
 4 void main(){
 5     //待解析字符串 
 6     char *json_str="{\"key1\":\"dongxiaodong\",\"key2\":1998,\"key3\":55778}";
 7     
 8     //輸出原字符串
 9     printf("原字符串:%s\r\n",json_str); 
10 
11     //解析成json對象 
12     cJSON * json_obj = cJSON_Parse(json_str); 
13 
14     //格式輸出
15     char *json_print_str=NULL;
16     json_print_str=cJSON_Print(json_obj);
17     printf("\r\n輸出內容:\r\n\r\n%s\r\n",json_print_str);
18     
19     //釋放資源 
20     free(json_print_str);
21     
22     //釋放資源 
23     cJSON_Delete(json_obj);
24 }

json打包

cJSON_CreateObject函數可創建一個根數據項,在此之後就可以添加各種數據類型的子節點了,使用完成后需要通過cJSON_Delete()釋放內存。

創建一層級的json

 代碼:

 1 #include <stdio.h>
 2 #include "cJSON.h"
 3 #include "cJSON.c"
 4 void main(){
 5     cJSON *root_obj = NULL;//根,json對象 
 6     char *out_str = NULL; //輸出結果 
 7     root_obj =cJSON_CreateObject();//創建 
 8      //添加一個字符串,參數(根對象,鍵,值) 
 9     cJSON_AddStringToObject(root_obj, "key1", "dongxiaodong");
10     //添加一個整型,參數(根對象,鍵,值) 
11     cJSON_AddNumberToObject(root_obj, "key2",1998);
12     //添加一個浮點型,參數(根對象,鍵,值) 
13     cJSON_AddNumberToObject(root_obj, "key3",22.33);
14     //添加一個bool類型,參數(根對象,鍵,值) 
15     //bool值可以是0/1或false/true 
16     cJSON_AddBoolToObject(root_obj, "key4",0);
17     //將json對象打包成字符串 
18     out_str = cJSON_PrintUnformatted(root_obj);
19     //銷毀json對象,釋放內存 
20     cJSON_Delete(root_obj); 
21     //輸出值:{"key1":"dongxiaodong","key2":1998,"key3":22.33,"key4":false} 
22     printf("%s",out_str);
23 }

類型創建函數還有:

cJSON_AddNullToObject(cJSON * const object, const char * const name);

cJSON_AddTrueToObject(cJSON * const object, const char * const name);

cJSON_AddFalseToObject(cJSON * const object, const char * const name);

cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);

cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);

cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);

cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);

cJSON_AddObjectToObject(cJSON * const object, const char * const name);

cJSON_AddArrayToObject(cJSON * const object, const char * const name);

創建多層級的json

 代碼:

 1 #include <stdio.h>
 2 #include "cJSON.h"
 3 #include "cJSON.c"
 4 void main(){
 5     cJSON *root_obj = NULL;//根,json對象 
 6     cJSON *item_obj = NULL;//二級json對象 
 7     char *out_str = NULL; //輸出結果 
 8     
 9     root_obj =cJSON_CreateObject();//創建 
10      //添加一個字符串,參數(根對象,鍵,值) 
11     cJSON_AddStringToObject(root_obj, "key1", "dongxiaodong");
12     //添加一個整型,參數(根對象,鍵,值) 
13     cJSON_AddNumberToObject(root_obj, "key2",1998);
14     
15     //創建一個子json對象
16     item_obj= cJSON_AddObjectToObject(root_obj,"myson"); 
17     //向孩子對象中添加內容
18     cJSON_AddStringToObject(item_obj, "sonkey1", "東小東"); 
19     cJSON_AddNumberToObject(item_obj, "sonkey2",2020);
20       
21     //將json對象打包成字符串 
22     out_str = cJSON_PrintUnformatted(root_obj);
23     //銷毀json對象,釋放內存 
24     cJSON_Delete(root_obj); 
25     //輸出值:{"key1":"dongxiaodong","key2":1998,"myson":{"sonkey1":"東小東","sonkey2":2020}}
26     printf("%s",out_str);
27 }

創建多層json(數組形式)

 

 代碼

 1 #include <stdio.h>
 2 #include "cJSON.h"
 3 #include "cJSON.c"
 4 void main(){
 5     cJSON *root_obj = NULL;//根,json對象 
 6     cJSON *item_obj = NULL;//數組對象 
 7     char *out_str = NULL; //輸出結果 
 8     
 9     root_obj =cJSON_CreateObject();//創建 
10      //添加一個字符串,參數(根對象,鍵,值) 
11     cJSON_AddStringToObject(root_obj, "key1", "dongxiaodong");
12     //添加一個整型,參數(根對象,鍵,值) 
13     cJSON_AddNumberToObject(root_obj, "key2",1998);
14     
15     //創建一個子數組對象 
16     item_obj= cJSON_AddArrayToObject(root_obj,"myson"); 
17     //向數組對象中添加內容
18     cJSON_AddItemToArray(item_obj,cJSON_CreateTrue()); 
19     cJSON_AddItemToArray(item_obj,cJSON_CreateNumber(22));
20       
21     //將json對象打包成字符串 
22     out_str = cJSON_PrintUnformatted(root_obj);
23     //銷毀json對象,釋放內存 
24     cJSON_Delete(root_obj); 
25     //輸出值:{"key1":"dongxiaodong","key2":1998,"myson":[true,22]}
26     printf("%s",out_str);
27 }

創建的對象還可以是下面這些

cJSON_CreateNull(void);

cJSON_CreateTrue(void);

cJSON_CreateFalse(void);

cJSON_CreateBool(cJSON_bool boolean);

cJSON_CreateNumber(double num);

cJSON_CreateString(const char *string);

cJSON_CreateRaw(const char *raw);

cJSON_CreateArray(void);

cJSON_CreateObject(void);

創建混合json

 代碼

 1 #include <stdio.h>
 2 #include "cJSON.h"
 3 #include "cJSON.c"
 4 void main(){
 5     cJSON *root_obj = NULL;//根,json對象 
 6     cJSON *son_obj=NULL; 
 7     cJSON *item_obj = NULL;//數組對象 
 8     char *out_str = NULL; //輸出結果 
 9     
10     //根對象 
11     root_obj =cJSON_CreateObject();//創建 
12     //添加一個字符串,參數(根對象,鍵,值) 
13     cJSON_AddStringToObject(root_obj, "key1", "dongxiaodong");
14     //添加一個整型,參數(根對象,鍵,值) 
15     cJSON_AddNumberToObject(root_obj, "key2",1998);
16     
17     //創建一個子數組對象 
18     item_obj= cJSON_AddArrayToObject(root_obj,"myson"); 
19     //向數組對象中添加內容
20     cJSON_AddItemToArray(item_obj,cJSON_CreateTrue()); 
21     cJSON_AddItemToArray(item_obj,cJSON_CreateNumber(22));
22     
23     //子對象 
24     son_obj =cJSON_CreateObject();//創建 
25     //添加一個字符串,參數(根對象,鍵,值) 
26     cJSON_AddStringToObject(son_obj, "son1", "dongxiaodong");
27     //添加一個整型,參數(根對象,鍵,值) 
28     cJSON_AddNumberToObject(son_obj, "son2",1998);
29     cJSON_AddItemToArray(item_obj,son_obj);
30     
31     //將json對象打包成字符串 
32     out_str = cJSON_PrintUnformatted(root_obj);
33     //銷毀json對象,釋放內存 
34     cJSON_Delete(root_obj); 
35     //輸出值:{"key1":"dongxiaodong","key2":1998,"myson":[true,22,{"son1":"dongxiaodong","son2":1998}]}
36     printf("%s",out_str);
37 }

json解析

解析一層級的json

 

代碼:

 1 #include <stdio.h>
 2 #include "cJSON.h"
 3 #include "cJSON.c"
 4 void main(){
 5     //待解析字符串 
 6     char *json_str="{\"key1\":\"dongxiaodong\",\"key2\":1998,\"key3\":22.33,\"key4\":true}";
 7     //解析成json對象 
 8     cJSON * json_obj = cJSON_Parse(json_str); 
 9     
10     //項存儲 
11     cJSON *item=NULL;
12     
13     //輸出原字符串
14     printf("原字符串:%s\r\n",json_str); 
15     
16     //獲取string類型 
17     item=cJSON_GetObjectItem(json_obj,"key1");  
18     printf("\r\nkey1:%s\r\n",item->valuestring);
19     cJSON_Delete(item);//釋放資源 
20      
21     //獲取数字 
22     item=cJSON_GetObjectItem(json_obj,"key2");  
23     printf("\r\nkey2:%d\r\n",item->valueint);
24     cJSON_Delete(item);//釋放資源 
25     
26     //獲取数字 
27     item=cJSON_GetObjectItem(json_obj,"key3");  
28     printf("\r\nkey3:%f\r\n",item->valuedouble);
29     cJSON_Delete(item);//釋放資源 
30     
31     //獲取bool 
32     item=cJSON_GetObjectItem(json_obj,"key4");  
33     printf("\r\nkey4:%d\r\n",item->valueint);
34     cJSON_Delete(item);//釋放資源 
35     
36     //是否資源 
37     cJSON_Delete(json_obj);
38 }

解析多層級的json

 

代碼

 1 #include <stdio.h>
 2 #include "cJSON.h"
 3 #include "cJSON.c"
 4 void main(){
 5     //待解析字符串 
 6     char *json_str="{\"key1\":\"dongxiaodong\",\"key2\":1998,\"myson\":{\"sonkey1\":\"東小東\",\"sonkey2\":2020}}";
 7     //解析成json對象 
 8     cJSON * json_obj = cJSON_Parse(json_str); 
 9     
10     //項存儲 
11     cJSON *item=NULL;
12     //內部項存儲
13     cJSON * item_item=NULL; 
14     
15     //輸出原字符串
16     printf("原字符串:%s\r\n",json_str); 
17     
18     //獲取string類型 
19     item=cJSON_GetObjectItem(json_obj,"key1");  
20     printf("\r\nkey1:%s\r\n",item->valuestring);
21     cJSON_Delete(item);//釋放資源 
22      
23     //獲取数字 
24     item=cJSON_GetObjectItem(json_obj,"key2");  
25     printf("\r\nkey2:%d\r\n",item->valueint);
26     cJSON_Delete(item);//釋放資源 
27     
28     //子串
29     item=cJSON_GetObjectItem(json_obj,"myson");  
30     item_item=cJSON_GetObjectItem(item,"sonkey1"); 
31     printf("\r\nmyson(sonkey1):%s\r\n",item_item->valuestring);
32     cJSON_Delete(item_item);//釋放資源 
33     
34     item_item=cJSON_GetObjectItem(item,"sonkey2"); 
35     printf("\r\nmyson(sonkey2):%d\r\n",item_item->valueint);
36     cJSON_Delete(item_item);//釋放資源 
37     
38     cJSON_Delete(item);//釋放資源 
39     
40     //釋放資源 
41     cJSON_Delete(json_obj);
42 }

 解析多層json(數組形式)

  

代碼

 1 #include <stdio.h>
 2 #include "cJSON.h"
 3 #include "cJSON.c"
 4 void main(){
 5     //待解析字符串 
 6     char *json_str="{\"key1\":\"dongxiaodong\",\"key2\":1998,\"myson\":[true,113]}";
 7     //解析成json對象 
 8     cJSON * json_obj = cJSON_Parse(json_str); 
 9     
10     //項存儲 
11     cJSON *item=NULL;
12     //內部項存儲
13     cJSON * item_item=NULL; 
14     
15     //輸出原字符串
16     printf("原字符串:%s\r\n",json_str); 
17     
18     //獲取string類型 
19     item=cJSON_GetObjectItem(json_obj,"key1");  
20     printf("\r\nkey1:%s\r\n",item->valuestring);
21     cJSON_Delete(item);//釋放資源 
22      
23     //獲取数字 
24     item=cJSON_GetObjectItem(json_obj,"key2");  
25     printf("\r\nkey2:%d\r\n",item->valueint);
26     cJSON_Delete(item);//釋放資源 
27     
28     //獲取子串 
29     item=cJSON_GetObjectItem(json_obj,"myson"); 
30     
31     //輸出數組大小 
32     printf("\r\n數組大小:%d\r\n",cJSON_GetArraySize(item));
33     
34     //輸出項1內容 
35     item_item=cJSON_GetArrayItem(item,0); 
36     printf("\r\nmyson(0):%d\r\n",item_item->valueint);
37     //cJSON_Delete(item_item);//釋放資源 
38     
39     //輸出項2內容 
40     item_item=cJSON_GetArrayItem(item,1);
41     printf("\r\nmyson(1):%d\r\n",item_item->valueint);
42     cJSON_Delete(item_item);//釋放資源 
43     
44     cJSON_Delete(item);//釋放資源 
45     
46     //釋放資源 
47     cJSON_Delete(json_obj);
48 }

  解析混合json

代碼

 1 #include <stdio.h>
 2 #include "cJSON.h"
 3 #include "cJSON.c"
 4 void main(){
 5     //待解析字符串 
 6     char *json_str="{\"key1\":\"dongxiaodong\",\"key2\":1998,\"myson\":[true,22,{\"son1\":\"dongxiaodong\",\"son2\":1998}]}";
 7     //解析成json對象 
 8     cJSON * json_obj = cJSON_Parse(json_str); 
 9     
10     //項存儲 
11     cJSON *item=NULL;
12     //內部項存儲
13     cJSON * item_item=NULL; 
14     
15     //輸出原字符串
16     printf("原字符串:%s\r\n",json_str); 
17     
18     //獲取string類型 
19     item=cJSON_GetObjectItem(json_obj,"key1");  
20     printf("\r\nkey1:%s\r\n",item->valuestring);
21     cJSON_Delete(item);//釋放資源 
22      
23     //獲取数字 
24     item=cJSON_GetObjectItem(json_obj,"key2");  
25     printf("\r\nkey2:%d\r\n",item->valueint);
26     cJSON_Delete(item);//釋放資源 
27     
28     //獲取子串 
29     item=cJSON_GetObjectItem(json_obj,"myson"); 
30     
31     //輸出數組大小 
32     printf("\r\n數組大小:%d\r\n",cJSON_GetArraySize(item));
33     
34     //輸出項1內容 
35     item_item=cJSON_GetArrayItem(item,0); 
36     printf("\r\nmyson(0):%d\r\n",item_item->valueint);
37     //cJSON_Delete(item_item);//釋放資源 
38     
39     //輸出項2內容 
40     item_item=cJSON_GetArrayItem(item,1);
41     printf("\r\nmyson(1):%d\r\n",item_item->valueint);
42     cJSON_Delete(item_item);//釋放資源 
43     
44     //項3內容
45     item_item=cJSON_GetArrayItem(item,2); 
46     cJSON *item_item_son=NULL;
47     
48     item_item_son =cJSON_GetObjectItem(item_item,"son1");  
49     printf("\r\nmyson(2)(son1):%s\r\n",item_item_son->valuestring);
50     cJSON_Delete(item_item_son);//釋放資源 
51     
52     item_item_son =cJSON_GetObjectItem(item_item,"son2");  
53     printf("\r\nmyson(2)(son2):%d\r\n",item_item_son->valueint);
54     cJSON_Delete(item_item_son);//釋放資源
55     cJSON_Delete(item_item);//釋放資源   
56     
57     cJSON_Delete(item);//釋放資源 
58     
59     //釋放資源 
60     cJSON_Delete(json_obj);
61 }

 附件:

cJSON.h

  1 /*
  2   Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
  3 
  4   Permission is hereby granted, free of charge, to any person obtaining a copy
  5   of this software and associated documentation files (the "Software"), to deal
  6   in the Software without restriction, including without limitation the rights
  7   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8   copies of the Software, and to permit persons to whom the Software is
  9   furnished to do so, subject to the following conditions:
 10 
 11   The above copyright notice and this permission notice shall be included in
 12   all copies or substantial portions of the Software.
 13 
 14   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 15   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 16   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 17   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 18   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 19   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 20   THE SOFTWARE.
 21 */
 22 
 23 #ifndef cJSON__h
 24 #define cJSON__h
 25 
 26 #ifdef __cplusplus
 27 extern "C"
 28 {
 29 #endif
 30 
 31 #if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
 32 #define __WINDOWS__
 33 #endif
 34 
 35 #ifdef __WINDOWS__
 36 
 37 /* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention.  For windows you have 3 define options:
 38 
 39 CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
 40 CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
 41 CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
 42 
 43 For *nix builds that support visibility attribute, you can define similar behavior by
 44 
 45 setting default visibility to hidden by adding
 46 -fvisibility=hidden (for gcc)
 47 or
 48 -xldscope=hidden (for sun cc)
 49 to CFLAGS
 50 
 51 then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
 52 
 53 */
 54 
 55 #define CJSON_CDECL __cdecl
 56 #define CJSON_STDCALL __stdcall
 57 
 58 /* export symbols by default, this is necessary for copy pasting the C and header file */
 59 #if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
 60 #define CJSON_EXPORT_SYMBOLS
 61 #endif
 62 
 63 #if defined(CJSON_HIDE_SYMBOLS)
 64 #define CJSON_PUBLIC(type)   type CJSON_STDCALL
 65 #elif defined(CJSON_EXPORT_SYMBOLS)
 66 #define CJSON_PUBLIC(type)   __declspec(dllexport) type CJSON_STDCALL
 67 #elif defined(CJSON_IMPORT_SYMBOLS)
 68 #define CJSON_PUBLIC(type)   __declspec(dllimport) type CJSON_STDCALL
 69 #endif
 70 #else /* !__WINDOWS__ */
 71 #define CJSON_CDECL
 72 #define CJSON_STDCALL
 73 
 74 #if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
 75 #define CJSON_PUBLIC(type)   __attribute__((visibility("default"))) type
 76 #else
 77 #define CJSON_PUBLIC(type) type
 78 #endif
 79 #endif
 80 
 81 /* project version */
 82 #define CJSON_VERSION_MAJOR 1
 83 #define CJSON_VERSION_MINOR 7
 84 #define CJSON_VERSION_PATCH 13
 85 
 86 #include <stddef.h>
 87 
 88 /* cJSON Types: */
 89 #define cJSON_Invalid (0)
 90 #define cJSON_False  (1 << 0)
 91 #define cJSON_True   (1 << 1)
 92 #define cJSON_NULL   (1 << 2)
 93 #define cJSON_Number (1 << 3)
 94 #define cJSON_String (1 << 4)
 95 #define cJSON_Array  (1 << 5)
 96 #define cJSON_Object (1 << 6)
 97 #define cJSON_Raw    (1 << 7) /* raw json */
 98 
 99 #define cJSON_IsReference 256
100 #define cJSON_StringIsConst 512
101 
102 /* The cJSON structure: */
103 typedef struct cJSON
104 {
105     /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
106     struct cJSON *next;
107     struct cJSON *prev;
108     /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
109     struct cJSON *child;
110 
111     /* The type of the item, as above. */
112     int type;
113 
114     /* The item's string, if type==cJSON_String  and type == cJSON_Raw */
115     char *valuestring;
116     /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
117     int valueint;
118     /* The item's number, if type==cJSON_Number */
119     double valuedouble;
120 
121     /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
122     char *string;
123 } cJSON;
124 
125 typedef struct cJSON_Hooks
126 {
127       /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
128       void *(CJSON_CDECL *malloc_fn)(size_t sz);
129       void (CJSON_CDECL *free_fn)(void *ptr);
130 } cJSON_Hooks;
131 
132 typedef int cJSON_bool;
133 
134 /* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
135  * This is to prevent stack overflows. */
136 #ifndef CJSON_NESTING_LIMIT
137 #define CJSON_NESTING_LIMIT 1000
138 #endif
139 
140 /* returns the version of cJSON as a string */
141 CJSON_PUBLIC(const char*) cJSON_Version(void);
142 
143 /* Supply malloc, realloc and free functions to cJSON */
144 CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
145 
146 /* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
147 /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
148 CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
149 CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
150 /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
151 /* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
152 CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
153 CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
154 
155 /* Render a cJSON entity to text for transfer/storage. */
156 CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
157 /* Render a cJSON entity to text for transfer/storage without any formatting. */
158 CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
159 /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
160 CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
161 /* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
162 /* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
163 CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
164 /* Delete a cJSON entity and all subentities. */
165 CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
166 
167 /* Returns the number of items in an array (or object). */
168 CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
169 /* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
170 CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
171 /* Get item "string" from object. Case insensitive. */
172 CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
173 CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
174 CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
175 /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
176 CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
177 
178 /* Check item type and return its value */
179 CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item);
180 CJSON_PUBLIC(double) cJSON_GetNumberValue(cJSON *item);
181 
182 /* These functions check the type of an item */
183 CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
184 CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
185 CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
186 CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
187 CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
188 CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
189 CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
190 CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
191 CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
192 CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
193 
194 /* These calls create a cJSON item of the appropriate type. */
195 CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
196 CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
197 CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
198 CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
199 CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
200 CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
201 /* raw json */
202 CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
203 CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
204 CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
205 
206 /* Create a string where valuestring references a string so
207  * it will not be freed by cJSON_Delete */
208 CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
209 /* Create an object/array that only references it's elements so
210  * they will not be freed by cJSON_Delete */
211 CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
212 CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
213 
214 /* These utilities create an Array of count items.
215  * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
216 CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
217 CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
218 CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
219 CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
220 
221 /* Append item to the specified array/object. */
222 CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
223 CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
224 /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
225  * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
226  * writing to `item->string` */
227 CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
228 /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
229 CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
230 CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
231 
232 /* Remove/Detach items from Arrays/Objects. */
233 CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
234 CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
235 CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
236 CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
237 CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
238 CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
239 CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
240 
241 /* Update array items. */
242 CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
243 CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
244 CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
245 CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
246 CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
247 
248 /* Duplicate a cJSON item */
249 CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
250 /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
251  * need to be released. With recurse!=0, it will duplicate any children connected to the item.
252  * The item->next and ->prev pointers are always zero on return from Duplicate. */
253 /* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
254  * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
255 CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
256 
257 /* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
258  * The input pointer json cannot point to a read-only address area, such as a string constant, 
259  * but should point to a readable and writable adress area. */
260 CJSON_PUBLIC(void) cJSON_Minify(char *json);
261 
262 /* Helper functions for creating and adding items to an object at the same time.
263  * They return the added item or NULL on failure. */
264 CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
265 CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
266 CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
267 CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
268 CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
269 CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
270 CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
271 CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
272 CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
273 
274 /* When assigning an integer value, it needs to be propagated to valuedouble too. */
275 #define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
276 /* helper for the cJSON_SetNumberValue macro */
277 CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
278 #define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
279 /* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
280 CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
281 
282 /* Macro for iterating over an array or object */
283 #define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
284 
285 /* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
286 CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
287 CJSON_PUBLIC(void) cJSON_free(void *object);
288 
289 #ifdef __cplusplus
290 }
291 #endif
292 
293 #endif

View Code

cJSON.c

   1 /*
   2   Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
   3 
   4   Permission is hereby granted, free of charge, to any person obtaining a copy
   5   of this software and associated documentation files (the "Software"), to deal
   6   in the Software without restriction, including without limitation the rights
   7   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   8   copies of the Software, and to permit persons to whom the Software is
   9   furnished to do so, subject to the following conditions:
  10 
  11   The above copyright notice and this permission notice shall be included in
  12   all copies or substantial portions of the Software.
  13 
  14   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20   THE SOFTWARE.
  21 */
  22 
  23 /* cJSON */
  24 /* JSON parser in C. */
  25 
  26 /* disable warnings about old C89 functions in MSVC */
  27 #if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
  28 #define _CRT_SECURE_NO_DEPRECATE
  29 #endif
  30 
  31 #ifdef __GNUC__
  32 #pragma GCC visibility push(default)
  33 #endif
  34 #if defined(_MSC_VER)
  35 #pragma warning (push)
  36 /* disable warning about single line comments in system headers */
  37 #pragma warning (disable : 4001)
  38 #endif
  39 
  40 #include <string.h>
  41 #include <stdio.h>
  42 #include <math.h>
  43 #include <stdlib.h>
  44 #include <limits.h>
  45 #include <ctype.h>
  46 #include <float.h>
  47 
  48 #ifdef ENABLE_LOCALES
  49 #include <locale.h>
  50 #endif
  51 
  52 #if defined(_MSC_VER)
  53 #pragma warning (pop)
  54 #endif
  55 #ifdef __GNUC__
  56 #pragma GCC visibility pop
  57 #endif
  58 
  59 #include "cJSON.h"
  60 
  61 /* define our own boolean type */
  62 #ifdef true
  63 #undef true
  64 #endif
  65 #define true ((cJSON_bool)1)
  66 
  67 #ifdef false
  68 #undef false
  69 #endif
  70 #define false ((cJSON_bool)0)
  71 
  72 /* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */
  73 #ifndef isinf
  74 #define isinf(d) (isnan((d - d)) && !isnan(d))
  75 #endif
  76 #ifndef isnan
  77 #define isnan(d) (d != d)
  78 #endif
  79 
  80 #ifndef NAN
  81 #define NAN 0.0/0.0
  82 #endif
  83 
  84 typedef struct {
  85     const unsigned char *json;
  86     size_t position;
  87 } error;
  88 static error global_error = { NULL, 0 };
  89 
  90 CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
  91 {
  92     return (const char*) (global_error.json + global_error.position);
  93 }
  94 
  95 CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) 
  96 {
  97     if (!cJSON_IsString(item)) 
  98     {
  99         return NULL;
 100     }
 101 
 102     return item->valuestring;
 103 }
 104 
 105 CJSON_PUBLIC(double) cJSON_GetNumberValue(cJSON *item) 
 106 {
 107     if (!cJSON_IsNumber(item)) 
 108     {
 109         return NAN;
 110     }
 111 
 112     return item->valuedouble;
 113 }
 114 
 115 /* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
 116 #if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 13)
 117     #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
 118 #endif
 119 
 120 CJSON_PUBLIC(const char*) cJSON_Version(void)
 121 {
 122     static char version[15];
 123     sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
 124 
 125     return version;
 126 }
 127 
 128 /* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
 129 static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
 130 {
 131     if ((string1 == NULL) || (string2 == NULL))
 132     {
 133         return 1;
 134     }
 135 
 136     if (string1 == string2)
 137     {
 138         return 0;
 139     }
 140 
 141     for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
 142     {
 143         if (*string1 == '\0')
 144         {
 145             return 0;
 146         }
 147     }
 148 
 149     return tolower(*string1) - tolower(*string2);
 150 }
 151 
 152 typedef struct internal_hooks
 153 {
 154     void *(CJSON_CDECL *allocate)(size_t size);
 155     void (CJSON_CDECL *deallocate)(void *pointer);
 156     void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
 157 } internal_hooks;
 158 
 159 #if defined(_MSC_VER)
 160 /* work around MSVC error C2322: '...' address of dllimport '...' is not static */
 161 static void * CJSON_CDECL internal_malloc(size_t size)
 162 {
 163     return malloc(size);
 164 }
 165 static void CJSON_CDECL internal_free(void *pointer)
 166 {
 167     free(pointer);
 168 }
 169 static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
 170 {
 171     return realloc(pointer, size);
 172 }
 173 #else
 174 #define internal_malloc malloc
 175 #define internal_free free
 176 #define internal_realloc realloc
 177 #endif
 178 
 179 /* strlen of character literals resolved at compile time */
 180 #define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
 181 
 182 static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
 183 
 184 static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
 185 {
 186     size_t length = 0;
 187     unsigned char *copy = NULL;
 188 
 189     if (string == NULL)
 190     {
 191         return NULL;
 192     }
 193 
 194     length = strlen((const char*)string) + sizeof("");
 195     copy = (unsigned char*)hooks->allocate(length);
 196     if (copy == NULL)
 197     {
 198         return NULL;
 199     }
 200     memcpy(copy, string, length);
 201 
 202     return copy;
 203 }
 204 
 205 CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
 206 {
 207     if (hooks == NULL)
 208     {
 209         /* Reset hooks */
 210         global_hooks.allocate = malloc;
 211         global_hooks.deallocate = free;
 212         global_hooks.reallocate = realloc;
 213         return;
 214     }
 215 
 216     global_hooks.allocate = malloc;
 217     if (hooks->malloc_fn != NULL)
 218     {
 219         global_hooks.allocate = hooks->malloc_fn;
 220     }
 221 
 222     global_hooks.deallocate = free;
 223     if (hooks->free_fn != NULL)
 224     {
 225         global_hooks.deallocate = hooks->free_fn;
 226     }
 227 
 228     /* use realloc only if both free and malloc are used */
 229     global_hooks.reallocate = NULL;
 230     if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
 231     {
 232         global_hooks.reallocate = realloc;
 233     }
 234 }
 235 
 236 /* Internal constructor. */
 237 static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
 238 {
 239     cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
 240     if (node)
 241     {
 242         memset(node, '\0', sizeof(cJSON));
 243     }
 244 
 245     return node;
 246 }
 247 
 248 /* Delete a cJSON structure. */
 249 CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
 250 {
 251     cJSON *next = NULL;
 252     while (item != NULL)
 253     {
 254         next = item->next;
 255         if (!(item->type & cJSON_IsReference) && (item->child != NULL))
 256         {
 257             cJSON_Delete(item->child);
 258         }
 259         if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
 260         {
 261             global_hooks.deallocate(item->valuestring);
 262         }
 263         if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
 264         {
 265             global_hooks.deallocate(item->string);
 266         }
 267         global_hooks.deallocate(item);
 268         item = next;
 269     }
 270 }
 271 
 272 /* get the decimal point character of the current locale */
 273 static unsigned char get_decimal_point(void)
 274 {
 275 #ifdef ENABLE_LOCALES
 276     struct lconv *lconv = localeconv();
 277     return (unsigned char) lconv->decimal_point[0];
 278 #else
 279     return '.';
 280 #endif
 281 }
 282 
 283 typedef struct
 284 {
 285     const unsigned char *content;
 286     size_t length;
 287     size_t offset;
 288     size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
 289     internal_hooks hooks;
 290 } parse_buffer;
 291 
 292 /* check if the given size is left to read in a given parse buffer (starting with 1) */
 293 #define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
 294 /* check if the buffer can be accessed at the given index (starting with 0) */
 295 #define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
 296 #define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
 297 /* get a pointer to the buffer at the position */
 298 #define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
 299 
 300 /* Parse the input text to generate a number, and populate the result into item. */
 301 static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
 302 {
 303     double number = 0;
 304     unsigned char *after_end = NULL;
 305     unsigned char number_c_string[64];
 306     unsigned char decimal_point = get_decimal_point();
 307     size_t i = 0;
 308 
 309     if ((input_buffer == NULL) || (input_buffer->content == NULL))
 310     {
 311         return false;
 312     }
 313 
 314     /* copy the number into a temporary buffer and replace '.' with the decimal point
 315      * of the current locale (for strtod)
 316      * This also takes care of '\0' not necessarily being available for marking the end of the input */
 317     for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
 318     {
 319         switch (buffer_at_offset(input_buffer)[i])
 320         {
 321             case '0':
 322             case '1':
 323             case '2':
 324             case '3':
 325             case '4':
 326             case '5':
 327             case '6':
 328             case '7':
 329             case '8':
 330             case '9':
 331             case '+':
 332             case '-':
 333             case 'e':
 334             case 'E':
 335                 number_c_string[i] = buffer_at_offset(input_buffer)[i];
 336                 break;
 337 
 338             case '.':
 339                 number_c_string[i] = decimal_point;
 340                 break;
 341 
 342             default:
 343                 goto loop_end;
 344         }
 345     }
 346 loop_end:
 347     number_c_string[i] = '\0';
 348 
 349     number = strtod((const char*)number_c_string, (char**)&after_end);
 350     if (number_c_string == after_end)
 351     {
 352         return false; /* parse_error */
 353     }
 354 
 355     item->valuedouble = number;
 356 
 357     /* use saturation in case of overflow */
 358     if (number >= INT_MAX)
 359     {
 360         item->valueint = INT_MAX;
 361     }
 362     else if (number <= (double)INT_MIN)
 363     {
 364         item->valueint = INT_MIN;
 365     }
 366     else
 367     {
 368         item->valueint = (int)number;
 369     }
 370 
 371     item->type = cJSON_Number;
 372 
 373     input_buffer->offset += (size_t)(after_end - number_c_string);
 374     return true;
 375 }
 376 
 377 /* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
 378 CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
 379 {
 380     if (number >= INT_MAX)
 381     {
 382         object->valueint = INT_MAX;
 383     }
 384     else if (number <= (double)INT_MIN)
 385     {
 386         object->valueint = INT_MIN;
 387     }
 388     else
 389     {
 390         object->valueint = (int)number;
 391     }
 392 
 393     return object->valuedouble = number;
 394 }
 395 
 396 CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
 397 {
 398     char *copy = NULL;
 399     /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
 400     if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference))
 401     {
 402         return NULL;
 403     }
 404     if (strlen(valuestring) <= strlen(object->valuestring))
 405     {
 406         strcpy(object->valuestring, valuestring);
 407         return object->valuestring;
 408     }
 409     copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks);
 410     if (copy == NULL)
 411     {
 412         return NULL;
 413     }
 414     if (object->valuestring != NULL)
 415     {
 416         cJSON_free(object->valuestring);
 417     }
 418     object->valuestring = copy;
 419 
 420     return copy;
 421 }
 422 
 423 typedef struct
 424 {
 425     unsigned char *buffer;
 426     size_t length;
 427     size_t offset;
 428     size_t depth; /* current nesting depth (for formatted printing) */
 429     cJSON_bool noalloc;
 430     cJSON_bool format; /* is this print a formatted print */
 431     internal_hooks hooks;
 432 } printbuffer;
 433 
 434 /* realloc printbuffer if necessary to have at least "needed" bytes more */
 435 static unsigned char* ensure(printbuffer * const p, size_t needed)
 436 {
 437     unsigned char *newbuffer = NULL;
 438     size_t newsize = 0;
 439 
 440     if ((p == NULL) || (p->buffer == NULL))
 441     {
 442         return NULL;
 443     }
 444 
 445     if ((p->length > 0) && (p->offset >= p->length))
 446     {
 447         /* make sure that offset is valid */
 448         return NULL;
 449     }
 450 
 451     if (needed > INT_MAX)
 452     {
 453         /* sizes bigger than INT_MAX are currently not supported */
 454         return NULL;
 455     }
 456 
 457     needed += p->offset + 1;
 458     if (needed <= p->length)
 459     {
 460         return p->buffer + p->offset;
 461     }
 462 
 463     if (p->noalloc) {
 464         return NULL;
 465     }
 466 
 467     /* calculate new buffer size */
 468     if (needed > (INT_MAX / 2))
 469     {
 470         /* overflow of int, use INT_MAX if possible */
 471         if (needed <= INT_MAX)
 472         {
 473             newsize = INT_MAX;
 474         }
 475         else
 476         {
 477             return NULL;
 478         }
 479     }
 480     else
 481     {
 482         newsize = needed * 2;
 483     }
 484 
 485     if (p->hooks.reallocate != NULL)
 486     {
 487         /* reallocate with realloc if available */
 488         newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
 489         if (newbuffer == NULL)
 490         {
 491             p->hooks.deallocate(p->buffer);
 492             p->length = 0;
 493             p->buffer = NULL;
 494 
 495             return NULL;
 496         }
 497     }
 498     else
 499     {
 500         /* otherwise reallocate manually */
 501         newbuffer = (unsigned char*)p->hooks.allocate(newsize);
 502         if (!newbuffer)
 503         {
 504             p->hooks.deallocate(p->buffer);
 505             p->length = 0;
 506             p->buffer = NULL;
 507 
 508             return NULL;
 509         }
 510         if (newbuffer)
 511         {
 512             memcpy(newbuffer, p->buffer, p->offset + 1);
 513         }
 514         p->hooks.deallocate(p->buffer);
 515     }
 516     p->length = newsize;
 517     p->buffer = newbuffer;
 518 
 519     return newbuffer + p->offset;
 520 }
 521 
 522 /* calculate the new length of the string in a printbuffer and update the offset */
 523 static void update_offset(printbuffer * const buffer)
 524 {
 525     const unsigned char *buffer_pointer = NULL;
 526     if ((buffer == NULL) || (buffer->buffer == NULL))
 527     {
 528         return;
 529     }
 530     buffer_pointer = buffer->buffer + buffer->offset;
 531 
 532     buffer->offset += strlen((const char*)buffer_pointer);
 533 }
 534 
 535 /* securely comparison of floating-point variables */
 536 static cJSON_bool compare_double(double a, double b)
 537 {
 538     double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
 539     return (fabs(a - b) <= maxVal * DBL_EPSILON);
 540 }
 541 
 542 /* Render the number nicely from the given item into a string. */
 543 static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
 544 {
 545     unsigned char *output_pointer = NULL;
 546     double d = item->valuedouble;
 547     int length = 0;
 548     size_t i = 0;
 549     unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */
 550     unsigned char decimal_point = get_decimal_point();
 551     double test = 0.0;
 552 
 553     if (output_buffer == NULL)
 554     {
 555         return false;
 556     }
 557 
 558     /* This checks for NaN and Infinity */
 559     if (isnan(d) || isinf(d))
 560     {
 561         length = sprintf((char*)number_buffer, "null");
 562     }
 563     else
 564     {
 565         /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
 566         length = sprintf((char*)number_buffer, "%1.15g", d);
 567 
 568         /* Check whether the original double can be recovered */
 569         if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
 570         {
 571             /* If not, print with 17 decimal places of precision */
 572             length = sprintf((char*)number_buffer, "%1.17g", d);
 573         }
 574     }
 575 
 576     /* sprintf failed or buffer overrun occurred */
 577     if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
 578     {
 579         return false;
 580     }
 581 
 582     /* reserve appropriate space in the output */
 583     output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
 584     if (output_pointer == NULL)
 585     {
 586         return false;
 587     }
 588 
 589     /* copy the printed number to the output and replace locale
 590      * dependent decimal point with '.' */
 591     for (i = 0; i < ((size_t)length); i++)
 592     {
 593         if (number_buffer[i] == decimal_point)
 594         {
 595             output_pointer[i] = '.';
 596             continue;
 597         }
 598 
 599         output_pointer[i] = number_buffer[i];
 600     }
 601     output_pointer[i] = '\0';
 602 
 603     output_buffer->offset += (size_t)length;
 604 
 605     return true;
 606 }
 607 
 608 /* parse 4 digit hexadecimal number */
 609 static unsigned parse_hex4(const unsigned char * const input)
 610 {
 611     unsigned int h = 0;
 612     size_t i = 0;
 613 
 614     for (i = 0; i < 4; i++)
 615     {
 616         /* parse digit */
 617         if ((input[i] >= '0') && (input[i] <= '9'))
 618         {
 619             h += (unsigned int) input[i] - '0';
 620         }
 621         else if ((input[i] >= 'A') && (input[i] <= 'F'))
 622         {
 623             h += (unsigned int) 10 + input[i] - 'A';
 624         }
 625         else if ((input[i] >= 'a') && (input[i] <= 'f'))
 626         {
 627             h += (unsigned int) 10 + input[i] - 'a';
 628         }
 629         else /* invalid */
 630         {
 631             return 0;
 632         }
 633 
 634         if (i < 3)
 635         {
 636             /* shift left to make place for the next nibble */
 637             h = h << 4;
 638         }
 639     }
 640 
 641     return h;
 642 }
 643 
 644 /* converts a UTF-16 literal to UTF-8
 645  * A literal can be one or two sequences of the form \uXXXX */
 646 static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
 647 {
 648     long unsigned int codepoint = 0;
 649     unsigned int first_code = 0;
 650     const unsigned char *first_sequence = input_pointer;
 651     unsigned char utf8_length = 0;
 652     unsigned char utf8_position = 0;
 653     unsigned char sequence_length = 0;
 654     unsigned char first_byte_mark = 0;
 655 
 656     if ((input_end - first_sequence) < 6)
 657     {
 658         /* input ends unexpectedly */
 659         goto fail;
 660     }
 661 
 662     /* get the first utf16 sequence */
 663     first_code = parse_hex4(first_sequence + 2);
 664 
 665     /* check that the code is valid */
 666     if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
 667     {
 668         goto fail;
 669     }
 670 
 671     /* UTF16 surrogate pair */
 672     if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
 673     {
 674         const unsigned char *second_sequence = first_sequence + 6;
 675         unsigned int second_code = 0;
 676         sequence_length = 12; /* \uXXXX\uXXXX */
 677 
 678         if ((input_end - second_sequence) < 6)
 679         {
 680             /* input ends unexpectedly */
 681             goto fail;
 682         }
 683 
 684         if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
 685         {
 686             /* missing second half of the surrogate pair */
 687             goto fail;
 688         }
 689 
 690         /* get the second utf16 sequence */
 691         second_code = parse_hex4(second_sequence + 2);
 692         /* check that the code is valid */
 693         if ((second_code < 0xDC00) || (second_code > 0xDFFF))
 694         {
 695             /* invalid second half of the surrogate pair */
 696             goto fail;
 697         }
 698 
 699 
 700         /* calculate the unicode codepoint from the surrogate pair */
 701         codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
 702     }
 703     else
 704     {
 705         sequence_length = 6; /* \uXXXX */
 706         codepoint = first_code;
 707     }
 708 
 709     /* encode as UTF-8
 710      * takes at maximum 4 bytes to encode:
 711      * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
 712     if (codepoint < 0x80)
 713     {
 714         /* normal ascii, encoding 0xxxxxxx */
 715         utf8_length = 1;
 716     }
 717     else if (codepoint < 0x800)
 718     {
 719         /* two bytes, encoding 110xxxxx 10xxxxxx */
 720         utf8_length = 2;
 721         first_byte_mark = 0xC0; /* 11000000 */
 722     }
 723     else if (codepoint < 0x10000)
 724     {
 725         /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
 726         utf8_length = 3;
 727         first_byte_mark = 0xE0; /* 11100000 */
 728     }
 729     else if (codepoint <= 0x10FFFF)
 730     {
 731         /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
 732         utf8_length = 4;
 733         first_byte_mark = 0xF0; /* 11110000 */
 734     }
 735     else
 736     {
 737         /* invalid unicode codepoint */
 738         goto fail;
 739     }
 740 
 741     /* encode as utf8 */
 742     for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
 743     {
 744         /* 10xxxxxx */
 745         (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
 746         codepoint >>= 6;
 747     }
 748     /* encode first byte */
 749     if (utf8_length > 1)
 750     {
 751         (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
 752     }
 753     else
 754     {
 755         (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
 756     }
 757 
 758     *output_pointer += utf8_length;
 759 
 760     return sequence_length;
 761 
 762 fail:
 763     return 0;
 764 }
 765 
 766 /* Parse the input text into an unescaped cinput, and populate item. */
 767 static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
 768 {
 769     const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
 770     const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
 771     unsigned char *output_pointer = NULL;
 772     unsigned char *output = NULL;
 773 
 774     /* not a string */
 775     if (buffer_at_offset(input_buffer)[0] != '\"')
 776     {
 777         goto fail;
 778     }
 779 
 780     {
 781         /* calculate approximate size of the output (overestimate) */
 782         size_t allocation_length = 0;
 783         size_t skipped_bytes = 0;
 784         while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
 785         {
 786             /* is escape sequence */
 787             if (input_end[0] == '\\')
 788             {
 789                 if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
 790                 {
 791                     /* prevent buffer overflow when last input character is a backslash */
 792                     goto fail;
 793                 }
 794                 skipped_bytes++;
 795                 input_end++;
 796             }
 797             input_end++;
 798         }
 799         if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
 800         {
 801             goto fail; /* string ended unexpectedly */
 802         }
 803 
 804         /* This is at most how much we need for the output */
 805         allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
 806         output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
 807         if (output == NULL)
 808         {
 809             goto fail; /* allocation failure */
 810         }
 811     }
 812 
 813     output_pointer = output;
 814     /* loop through the string literal */
 815     while (input_pointer < input_end)
 816     {
 817         if (*input_pointer != '\\')
 818         {
 819             *output_pointer++ = *input_pointer++;
 820         }
 821         /* escape sequence */
 822         else
 823         {
 824             unsigned char sequence_length = 2;
 825             if ((input_end - input_pointer) < 1)
 826             {
 827                 goto fail;
 828             }
 829 
 830             switch (input_pointer[1])
 831             {
 832                 case 'b':
 833                     *output_pointer++ = '\b';
 834                     break;
 835                 case 'f':
 836                     *output_pointer++ = '\f';
 837                     break;
 838                 case 'n':
 839                     *output_pointer++ = '\n';
 840                     break;
 841                 case 'r':
 842                     *output_pointer++ = '\r';
 843                     break;
 844                 case 't':
 845                     *output_pointer++ = '\t';
 846                     break;
 847                 case '\"':
 848                 case '\\':
 849                 case '/':
 850                     *output_pointer++ = input_pointer[1];
 851                     break;
 852 
 853                 /* UTF-16 literal */
 854                 case 'u':
 855                     sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
 856                     if (sequence_length == 0)
 857                     {
 858                         /* failed to convert UTF16-literal to UTF-8 */
 859                         goto fail;
 860                     }
 861                     break;
 862 
 863                 default:
 864                     goto fail;
 865             }
 866             input_pointer += sequence_length;
 867         }
 868     }
 869 
 870     /* zero terminate the output */
 871     *output_pointer = '\0';
 872 
 873     item->type = cJSON_String;
 874     item->valuestring = (char*)output;
 875 
 876     input_buffer->offset = (size_t) (input_end - input_buffer->content);
 877     input_buffer->offset++;
 878 
 879     return true;
 880 
 881 fail:
 882     if (output != NULL)
 883     {
 884         input_buffer->hooks.deallocate(output);
 885     }
 886 
 887     if (input_pointer != NULL)
 888     {
 889         input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
 890     }
 891 
 892     return false;
 893 }
 894 
 895 /* Render the cstring provided to an escaped version that can be printed. */
 896 static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
 897 {
 898     const unsigned char *input_pointer = NULL;
 899     unsigned char *output = NULL;
 900     unsigned char *output_pointer = NULL;
 901     size_t output_length = 0;
 902     /* numbers of additional characters needed for escaping */
 903     size_t escape_characters = 0;
 904 
 905     if (output_buffer == NULL)
 906     {
 907         return false;
 908     }
 909 
 910     /* empty string */
 911     if (input == NULL)
 912     {
 913         output = ensure(output_buffer, sizeof("\"\""));
 914         if (output == NULL)
 915         {
 916             return false;
 917         }
 918         strcpy((char*)output, "\"\"");
 919 
 920         return true;
 921     }
 922 
 923     /* set "flag" to 1 if something needs to be escaped */
 924     for (input_pointer = input; *input_pointer; input_pointer++)
 925     {
 926         switch (*input_pointer)
 927         {
 928             case '\"':
 929             case '\\':
 930             case '\b':
 931             case '\f':
 932             case '\n':
 933             case '\r':
 934             case '\t':
 935                 /* one character escape sequence */
 936                 escape_characters++;
 937                 break;
 938             default:
 939                 if (*input_pointer < 32)
 940                 {
 941                     /* UTF-16 escape sequence uXXXX */
 942                     escape_characters += 5;
 943                 }
 944                 break;
 945         }
 946     }
 947     output_length = (size_t)(input_pointer - input) + escape_characters;
 948 
 949     output = ensure(output_buffer, output_length + sizeof("\"\""));
 950     if (output == NULL)
 951     {
 952         return false;
 953     }
 954 
 955     /* no characters have to be escaped */
 956     if (escape_characters == 0)
 957     {
 958         output[0] = '\"';
 959         memcpy(output + 1, input, output_length);
 960         output[output_length + 1] = '\"';
 961         output[output_length + 2] = '\0';
 962 
 963         return true;
 964     }
 965 
 966     output[0] = '\"';
 967     output_pointer = output + 1;
 968     /* copy the string */
 969     for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
 970     {
 971         if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
 972         {
 973             /* normal character, copy */
 974             *output_pointer = *input_pointer;
 975         }
 976         else
 977         {
 978             /* character needs to be escaped */
 979             *output_pointer++ = '\\';
 980             switch (*input_pointer)
 981             {
 982                 case '\\':
 983                     *output_pointer = '\\';
 984                     break;
 985                 case '\"':
 986                     *output_pointer = '\"';
 987                     break;
 988                 case '\b':
 989                     *output_pointer = 'b';
 990                     break;
 991                 case '\f':
 992                     *output_pointer = 'f';
 993                     break;
 994                 case '\n':
 995                     *output_pointer = 'n';
 996                     break;
 997                 case '\r':
 998                     *output_pointer = 'r';
 999                     break;
1000                 case '\t':
1001                     *output_pointer = 't';
1002                     break;
1003                 default:
1004                     /* escape and print as unicode codepoint */
1005                     sprintf((char*)output_pointer, "u%04x", *input_pointer);
1006                     output_pointer += 4;
1007                     break;
1008             }
1009         }
1010     }
1011     output[output_length + 1] = '\"';
1012     output[output_length + 2] = '\0';
1013 
1014     return true;
1015 }
1016 
1017 /* Invoke print_string_ptr (which is useful) on an item. */
1018 static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
1019 {
1020     return print_string_ptr((unsigned char*)item->valuestring, p);
1021 }
1022 
1023 /* Predeclare these prototypes. */
1024 static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
1025 static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
1026 static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
1027 static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
1028 static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
1029 static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);
1030 
1031 /* Utility to jump whitespace and cr/lf */
1032 static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
1033 {
1034     if ((buffer == NULL) || (buffer->content == NULL))
1035     {
1036         return NULL;
1037     }
1038 
1039     if (cannot_access_at_index(buffer, 0))
1040     {
1041         return buffer;
1042     }
1043 
1044     while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
1045     {
1046        buffer->offset++;
1047     }
1048 
1049     if (buffer->offset == buffer->length)
1050     {
1051         buffer->offset--;
1052     }
1053 
1054     return buffer;
1055 }
1056 
1057 /* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
1058 static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
1059 {
1060     if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
1061     {
1062         return NULL;
1063     }
1064 
1065     if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
1066     {
1067         buffer->offset += 3;
1068     }
1069 
1070     return buffer;
1071 }
1072 
1073 CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
1074 {
1075     size_t buffer_length;
1076 
1077     if (NULL == value)
1078     {
1079         return NULL;
1080     }
1081 
1082     /* Adding null character size due to require_null_terminated. */
1083     buffer_length = strlen(value) + sizeof("");
1084 
1085     return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
1086 }
1087 
1088 /* Parse an object - create a new root, and populate. */
1089 CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
1090 {
1091     parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
1092     cJSON *item = NULL;
1093 
1094     /* reset error position */
1095     global_error.json = NULL;
1096     global_error.position = 0;
1097 
1098     if (value == NULL || 0 == buffer_length)
1099     {
1100         goto fail;
1101     }
1102 
1103     buffer.content = (const unsigned char*)value;
1104     buffer.length = buffer_length; 
1105     buffer.offset = 0;
1106     buffer.hooks = global_hooks;
1107 
1108     item = cJSON_New_Item(&global_hooks);
1109     if (item == NULL) /* memory fail */
1110     {
1111         goto fail;
1112     }
1113 
1114     if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
1115     {
1116         /* parse failure. ep is set. */
1117         goto fail;
1118     }
1119 
1120     /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
1121     if (require_null_terminated)
1122     {
1123         buffer_skip_whitespace(&buffer);
1124         if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
1125         {
1126             goto fail;
1127         }
1128     }
1129     if (return_parse_end)
1130     {
1131         *return_parse_end = (const char*)buffer_at_offset(&buffer);
1132     }
1133 
1134     return item;
1135 
1136 fail:
1137     if (item != NULL)
1138     {
1139         cJSON_Delete(item);
1140     }
1141 
1142     if (value != NULL)
1143     {
1144         error local_error;
1145         local_error.json = (const unsigned char*)value;
1146         local_error.position = 0;
1147 
1148         if (buffer.offset < buffer.length)
1149         {
1150             local_error.position = buffer.offset;
1151         }
1152         else if (buffer.length > 0)
1153         {
1154             local_error.position = buffer.length - 1;
1155         }
1156 
1157         if (return_parse_end != NULL)
1158         {
1159             *return_parse_end = (const char*)local_error.json + local_error.position;
1160         }
1161 
1162         global_error = local_error;
1163     }
1164 
1165     return NULL;
1166 }
1167 
1168 /* Default options for cJSON_Parse */
1169 CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
1170 {
1171     return cJSON_ParseWithOpts(value, 0, 0);
1172 }
1173 
1174 CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
1175 {
1176     return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
1177 }
1178 
1179 #define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
1180 
1181 static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
1182 {
1183     static const size_t default_buffer_size = 256;
1184     printbuffer buffer[1];
1185     unsigned char *printed = NULL;
1186 
1187     memset(buffer, 0, sizeof(buffer));
1188 
1189     /* create buffer */
1190     buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
1191     buffer->length = default_buffer_size;
1192     buffer->format = format;
1193     buffer->hooks = *hooks;
1194     if (buffer->buffer == NULL)
1195     {
1196         goto fail;
1197     }
1198 
1199     /* print the value */
1200     if (!print_value(item, buffer))
1201     {
1202         goto fail;
1203     }
1204     update_offset(buffer);
1205 
1206     /* check if reallocate is available */
1207     if (hooks->reallocate != NULL)
1208     {
1209         printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
1210         if (printed == NULL) {
1211             goto fail;
1212         }
1213         buffer->buffer = NULL;
1214     }
1215     else /* otherwise copy the JSON over to a new buffer */
1216     {
1217         printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
1218         if (printed == NULL)
1219         {
1220             goto fail;
1221         }
1222         memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
1223         printed[buffer->offset] = '\0'; /* just to be sure */
1224 
1225         /* free the buffer */
1226         hooks->deallocate(buffer->buffer);
1227     }
1228 
1229     return printed;
1230 
1231 fail:
1232     if (buffer->buffer != NULL)
1233     {
1234         hooks->deallocate(buffer->buffer);
1235     }
1236 
1237     if (printed != NULL)
1238     {
1239         hooks->deallocate(printed);
1240     }
1241 
1242     return NULL;
1243 }
1244 
1245 /* Render a cJSON item/entity/structure to text. */
1246 CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
1247 {
1248     return (char*)print(item, true, &global_hooks);
1249 }
1250 
1251 CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
1252 {
1253     return (char*)print(item, false, &global_hooks);
1254 }
1255 
1256 CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
1257 {
1258     printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
1259 
1260     if (prebuffer < 0)
1261     {
1262         return NULL;
1263     }
1264 
1265     p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
1266     if (!p.buffer)
1267     {
1268         return NULL;
1269     }
1270 
1271     p.length = (size_t)prebuffer;
1272     p.offset = 0;
1273     p.noalloc = false;
1274     p.format = fmt;
1275     p.hooks = global_hooks;
1276 
1277     if (!print_value(item, &p))
1278     {
1279         global_hooks.deallocate(p.buffer);
1280         return NULL;
1281     }
1282 
1283     return (char*)p.buffer;
1284 }
1285 
1286 CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
1287 {
1288     printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
1289 
1290     if ((length < 0) || (buffer == NULL))
1291     {
1292         return false;
1293     }
1294 
1295     p.buffer = (unsigned char*)buffer;
1296     p.length = (size_t)length;
1297     p.offset = 0;
1298     p.noalloc = true;
1299     p.format = format;
1300     p.hooks = global_hooks;
1301 
1302     return print_value(item, &p);
1303 }
1304 
1305 /* Parser core - when encountering text, process appropriately. */
1306 static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
1307 {
1308     if ((input_buffer == NULL) || (input_buffer->content == NULL))
1309     {
1310         return false; /* no input */
1311     }
1312 
1313     /* parse the different types of values */
1314     /* null */
1315     if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
1316     {
1317         item->type = cJSON_NULL;
1318         input_buffer->offset += 4;
1319         return true;
1320     }
1321     /* false */
1322     if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
1323     {
1324         item->type = cJSON_False;
1325         input_buffer->offset += 5;
1326         return true;
1327     }
1328     /* true */
1329     if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
1330     {
1331         item->type = cJSON_True;
1332         item->valueint = 1;
1333         input_buffer->offset += 4;
1334         return true;
1335     }
1336     /* string */
1337     if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
1338     {
1339         return parse_string(item, input_buffer);
1340     }
1341     /* number */
1342     if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
1343     {
1344         return parse_number(item, input_buffer);
1345     }
1346     /* array */
1347     if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
1348     {
1349         return parse_array(item, input_buffer);
1350     }
1351     /* object */
1352     if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
1353     {
1354         return parse_object(item, input_buffer);
1355     }
1356 
1357     return false;
1358 }
1359 
1360 /* Render a value to text. */
1361 static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
1362 {
1363     unsigned char *output = NULL;
1364 
1365     if ((item == NULL) || (output_buffer == NULL))
1366     {
1367         return false;
1368     }
1369 
1370     switch ((item->type) & 0xFF)
1371     {
1372         case cJSON_NULL:
1373             output = ensure(output_buffer, 5);
1374             if (output == NULL)
1375             {
1376                 return false;
1377             }
1378             strcpy((char*)output, "null");
1379             return true;
1380 
1381         case cJSON_False:
1382             output = ensure(output_buffer, 6);
1383             if (output == NULL)
1384             {
1385                 return false;
1386             }
1387             strcpy((char*)output, "false");
1388             return true;
1389 
1390         case cJSON_True:
1391             output = ensure(output_buffer, 5);
1392             if (output == NULL)
1393             {
1394                 return false;
1395             }
1396             strcpy((char*)output, "true");
1397             return true;
1398 
1399         case cJSON_Number:
1400             return print_number(item, output_buffer);
1401 
1402         case cJSON_Raw:
1403         {
1404             size_t raw_length = 0;
1405             if (item->valuestring == NULL)
1406             {
1407                 return false;
1408             }
1409 
1410             raw_length = strlen(item->valuestring) + sizeof("");
1411             output = ensure(output_buffer, raw_length);
1412             if (output == NULL)
1413             {
1414                 return false;
1415             }
1416             memcpy(output, item->valuestring, raw_length);
1417             return true;
1418         }
1419 
1420         case cJSON_String:
1421             return print_string(item, output_buffer);
1422 
1423         case cJSON_Array:
1424             return print_array(item, output_buffer);
1425 
1426         case cJSON_Object:
1427             return print_object(item, output_buffer);
1428 
1429         default:
1430             return false;
1431     }
1432 }
1433 
1434 /* Build an array from input text. */
1435 static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
1436 {
1437     cJSON *head = NULL; /* head of the linked list */
1438     cJSON *current_item = NULL;
1439 
1440     if (input_buffer->depth >= CJSON_NESTING_LIMIT)
1441     {
1442         return false; /* to deeply nested */
1443     }
1444     input_buffer->depth++;
1445 
1446     if (buffer_at_offset(input_buffer)[0] != '[')
1447     {
1448         /* not an array */
1449         goto fail;
1450     }
1451 
1452     input_buffer->offset++;
1453     buffer_skip_whitespace(input_buffer);
1454     if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
1455     {
1456         /* empty array */
1457         goto success;
1458     }
1459 
1460     /* check if we skipped to the end of the buffer */
1461     if (cannot_access_at_index(input_buffer, 0))
1462     {
1463         input_buffer->offset--;
1464         goto fail;
1465     }
1466 
1467     /* step back to character in front of the first element */
1468     input_buffer->offset--;
1469     /* loop through the comma separated array elements */
1470     do
1471     {
1472         /* allocate next item */
1473         cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
1474         if (new_item == NULL)
1475         {
1476             goto fail; /* allocation failure */
1477         }
1478 
1479         /* attach next item to list */
1480         if (head == NULL)
1481         {
1482             /* start the linked list */
1483             current_item = head = new_item;
1484         }
1485         else
1486         {
1487             /* add to the end and advance */
1488             current_item->next = new_item;
1489             new_item->prev = current_item;
1490             current_item = new_item;
1491         }
1492 
1493         /* parse next value */
1494         input_buffer->offset++;
1495         buffer_skip_whitespace(input_buffer);
1496         if (!parse_value(current_item, input_buffer))
1497         {
1498             goto fail; /* failed to parse value */
1499         }
1500         buffer_skip_whitespace(input_buffer);
1501     }
1502     while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
1503 
1504     if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
1505     {
1506         goto fail; /* expected end of array */
1507     }
1508 
1509 success:
1510     input_buffer->depth--;
1511 
1512     item->type = cJSON_Array;
1513     item->child = head;
1514 
1515     input_buffer->offset++;
1516 
1517     return true;
1518 
1519 fail:
1520     if (head != NULL)
1521     {
1522         cJSON_Delete(head);
1523     }
1524 
1525     return false;
1526 }
1527 
1528 /* Render an array to text */
1529 static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
1530 {
1531     unsigned char *output_pointer = NULL;
1532     size_t length = 0;
1533     cJSON *current_element = item->child;
1534 
1535     if (output_buffer == NULL)
1536     {
1537         return false;
1538     }
1539 
1540     /* Compose the output array. */
1541     /* opening square bracket */
1542     output_pointer = ensure(output_buffer, 1);
1543     if (output_pointer == NULL)
1544     {
1545         return false;
1546     }
1547 
1548     *output_pointer = '[';
1549     output_buffer->offset++;
1550     output_buffer->depth++;
1551 
1552     while (current_element != NULL)
1553     {
1554         if (!print_value(current_element, output_buffer))
1555         {
1556             return false;
1557         }
1558         update_offset(output_buffer);
1559         if (current_element->next)
1560         {
1561             length = (size_t) (output_buffer->format ? 2 : 1);
1562             output_pointer = ensure(output_buffer, length + 1);
1563             if (output_pointer == NULL)
1564             {
1565                 return false;
1566             }
1567             *output_pointer++ = ',';
1568             if(output_buffer->format)
1569             {
1570                 *output_pointer++ = ' ';
1571             }
1572             *output_pointer = '\0';
1573             output_buffer->offset += length;
1574         }
1575         current_element = current_element->next;
1576     }
1577 
1578     output_pointer = ensure(output_buffer, 2);
1579     if (output_pointer == NULL)
1580     {
1581         return false;
1582     }
1583     *output_pointer++ = ']';
1584     *output_pointer = '\0';
1585     output_buffer->depth--;
1586 
1587     return true;
1588 }
1589 
1590 /* Build an object from the text. */
1591 static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
1592 {
1593     cJSON *head = NULL; /* linked list head */
1594     cJSON *current_item = NULL;
1595 
1596     if (input_buffer->depth >= CJSON_NESTING_LIMIT)
1597     {
1598         return false; /* to deeply nested */
1599     }
1600     input_buffer->depth++;
1601 
1602     if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
1603     {
1604         goto fail; /* not an object */
1605     }
1606 
1607     input_buffer->offset++;
1608     buffer_skip_whitespace(input_buffer);
1609     if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
1610     {
1611         goto success; /* empty object */
1612     }
1613 
1614     /* check if we skipped to the end of the buffer */
1615     if (cannot_access_at_index(input_buffer, 0))
1616     {
1617         input_buffer->offset--;
1618         goto fail;
1619     }
1620 
1621     /* step back to character in front of the first element */
1622     input_buffer->offset--;
1623     /* loop through the comma separated array elements */
1624     do
1625     {
1626         /* allocate next item */
1627         cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
1628         if (new_item == NULL)
1629         {
1630             goto fail; /* allocation failure */
1631         }
1632 
1633         /* attach next item to list */
1634         if (head == NULL)
1635         {
1636             /* start the linked list */
1637             current_item = head = new_item;
1638         }
1639         else
1640         {
1641             /* add to the end and advance */
1642             current_item->next = new_item;
1643             new_item->prev = current_item;
1644             current_item = new_item;
1645         }
1646 
1647         /* parse the name of the child */
1648         input_buffer->offset++;
1649         buffer_skip_whitespace(input_buffer);
1650         if (!parse_string(current_item, input_buffer))
1651         {
1652             goto fail; /* failed to parse name */
1653         }
1654         buffer_skip_whitespace(input_buffer);
1655 
1656         /* swap valuestring and string, because we parsed the name */
1657         current_item->string = current_item->valuestring;
1658         current_item->valuestring = NULL;
1659 
1660         if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
1661         {
1662             goto fail; /* invalid object */
1663         }
1664 
1665         /* parse the value */
1666         input_buffer->offset++;
1667         buffer_skip_whitespace(input_buffer);
1668         if (!parse_value(current_item, input_buffer))
1669         {
1670             goto fail; /* failed to parse value */
1671         }
1672         buffer_skip_whitespace(input_buffer);
1673     }
1674     while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
1675 
1676     if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
1677     {
1678         goto fail; /* expected end of object */
1679     }
1680 
1681 success:
1682     input_buffer->depth--;
1683 
1684     item->type = cJSON_Object;
1685     item->child = head;
1686 
1687     input_buffer->offset++;
1688     return true;
1689 
1690 fail:
1691     if (head != NULL)
1692     {
1693         cJSON_Delete(head);
1694     }
1695 
1696     return false;
1697 }
1698 
1699 /* Render an object to text. */
1700 static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
1701 {
1702     unsigned char *output_pointer = NULL;
1703     size_t length = 0;
1704     cJSON *current_item = item->child;
1705 
1706     if (output_buffer == NULL)
1707     {
1708         return false;
1709     }
1710 
1711     /* Compose the output: */
1712     length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
1713     output_pointer = ensure(output_buffer, length + 1);
1714     if (output_pointer == NULL)
1715     {
1716         return false;
1717     }
1718 
1719     *output_pointer++ = '{';
1720     output_buffer->depth++;
1721     if (output_buffer->format)
1722     {
1723         *output_pointer++ = '\n';
1724     }
1725     output_buffer->offset += length;
1726 
1727     while (current_item)
1728     {
1729         if (output_buffer->format)
1730         {
1731             size_t i;
1732             output_pointer = ensure(output_buffer, output_buffer->depth);
1733             if (output_pointer == NULL)
1734             {
1735                 return false;
1736             }
1737             for (i = 0; i < output_buffer->depth; i++)
1738             {
1739                 *output_pointer++ = '\t';
1740             }
1741             output_buffer->offset += output_buffer->depth;
1742         }
1743 
1744         /* print key */
1745         if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
1746         {
1747             return false;
1748         }
1749         update_offset(output_buffer);
1750 
1751         length = (size_t) (output_buffer->format ? 2 : 1);
1752         output_pointer = ensure(output_buffer, length);
1753         if (output_pointer == NULL)
1754         {
1755             return false;
1756         }
1757         *output_pointer++ = ':';
1758         if (output_buffer->format)
1759         {
1760             *output_pointer++ = '\t';
1761         }
1762         output_buffer->offset += length;
1763 
1764         /* print value */
1765         if (!print_value(current_item, output_buffer))
1766         {
1767             return false;
1768         }
1769         update_offset(output_buffer);
1770 
1771         /* print comma if not last */
1772         length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
1773         output_pointer = ensure(output_buffer, length + 1);
1774         if (output_pointer == NULL)
1775         {
1776             return false;
1777         }
1778         if (current_item->next)
1779         {
1780             *output_pointer++ = ',';
1781         }
1782 
1783         if (output_buffer->format)
1784         {
1785             *output_pointer++ = '\n';
1786         }
1787         *output_pointer = '\0';
1788         output_buffer->offset += length;
1789 
1790         current_item = current_item->next;
1791     }
1792 
1793     output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
1794     if (output_pointer == NULL)
1795     {
1796         return false;
1797     }
1798     if (output_buffer->format)
1799     {
1800         size_t i;
1801         for (i = 0; i < (output_buffer->depth - 1); i++)
1802         {
1803             *output_pointer++ = '\t';
1804         }
1805     }
1806     *output_pointer++ = '}';
1807     *output_pointer = '\0';
1808     output_buffer->depth--;
1809 
1810     return true;
1811 }
1812 
1813 /* Get Array size/item / object item. */
1814 CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
1815 {
1816     cJSON *child = NULL;
1817     size_t size = 0;
1818 
1819     if (array == NULL)
1820     {
1821         return 0;
1822     }
1823 
1824     child = array->child;
1825 
1826     while(child != NULL)
1827     {
1828         size++;
1829         child = child->next;
1830     }
1831 
1832     /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
1833 
1834     return (int)size;
1835 }
1836 
1837 static cJSON* get_array_item(const cJSON *array, size_t index)
1838 {
1839     cJSON *current_child = NULL;
1840 
1841     if (array == NULL)
1842     {
1843         return NULL;
1844     }
1845 
1846     current_child = array->child;
1847     while ((current_child != NULL) && (index > 0))
1848     {
1849         index--;
1850         current_child = current_child->next;
1851     }
1852 
1853     return current_child;
1854 }
1855 
1856 CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
1857 {
1858     if (index < 0)
1859     {
1860         return NULL;
1861     }
1862 
1863     return get_array_item(array, (size_t)index);
1864 }
1865 
1866 static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
1867 {
1868     cJSON *current_element = NULL;
1869 
1870     if ((object == NULL) || (name == NULL))
1871     {
1872         return NULL;
1873     }
1874 
1875     current_element = object->child;
1876     if (case_sensitive)
1877     {
1878         while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0))
1879         {
1880             current_element = current_element->next;
1881         }
1882     }
1883     else
1884     {
1885         while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0))
1886         {
1887             current_element = current_element->next;
1888         }
1889     }
1890 
1891     if ((current_element == NULL) || (current_element->string == NULL)) {
1892         return NULL;
1893     }
1894 
1895     return current_element;
1896 }
1897 
1898 CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
1899 {
1900     return get_object_item(object, string, false);
1901 }
1902 
1903 CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
1904 {
1905     return get_object_item(object, string, true);
1906 }
1907 
1908 CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
1909 {
1910     return cJSON_GetObjectItem(object, string) ? 1 : 0;
1911 }
1912 
1913 /* Utility for array list handling. */
1914 static void suffix_object(cJSON *prev, cJSON *item)
1915 {
1916     prev->next = item;
1917     item->prev = prev;
1918 }
1919 
1920 /* Utility for handling references. */
1921 static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
1922 {
1923     cJSON *reference = NULL;
1924     if (item == NULL)
1925     {
1926         return NULL;
1927     }
1928 
1929     reference = cJSON_New_Item(hooks);
1930     if (reference == NULL)
1931     {
1932         return NULL;
1933     }
1934 
1935     memcpy(reference, item, sizeof(cJSON));
1936     reference->string = NULL;
1937     reference->type |= cJSON_IsReference;
1938     reference->next = reference->prev = NULL;
1939     return reference;
1940 }
1941 
1942 static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
1943 {
1944     cJSON *child = NULL;
1945 
1946     if ((item == NULL) || (array == NULL) || (array == item))
1947     {
1948         return false;
1949     }
1950 
1951     child = array->child;
1952     /*
1953      * To find the last item in array quickly, we use prev in array
1954      */
1955     if (child == NULL)
1956     {
1957         /* list is empty, start new one */
1958         array->child = item;
1959         item->prev = item;
1960         item->next = NULL;
1961     }
1962     else
1963     {
1964         /* append to the end */
1965         if (child->prev)
1966         {
1967             suffix_object(child->prev, item);
1968             array->child->prev = item;
1969         }
1970         else
1971         {
1972             while (child->next)
1973             {
1974                 child = child->next;
1975             }
1976             suffix_object(child, item);
1977             array->child->prev = item;
1978         }
1979     }
1980 
1981     return true;
1982 }
1983 
1984 /* Add item to array/object. */
1985 CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item)
1986 {
1987     return add_item_to_array(array, item);
1988 }
1989 
1990 #if defined(__clang__) || (defined(__GNUC__)  && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
1991     #pragma GCC diagnostic push
1992 #endif
1993 #ifdef __GNUC__
1994 #pragma GCC diagnostic ignored "-Wcast-qual"
1995 #endif
1996 /* helper function to cast away const */
1997 static void* cast_away_const(const void* string)
1998 {
1999     return (void*)string;
2000 }
2001 #if defined(__clang__) || (defined(__GNUC__)  && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
2002     #pragma GCC diagnostic pop
2003 #endif
2004 
2005 
2006 static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
2007 {
2008     char *new_key = NULL;
2009     int new_type = cJSON_Invalid;
2010 
2011     if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
2012     {
2013         return false;
2014     }
2015 
2016     if (constant_key)
2017     {
2018         new_key = (char*)cast_away_const(string);
2019         new_type = item->type | cJSON_StringIsConst;
2020     }
2021     else
2022     {
2023         new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
2024         if (new_key == NULL)
2025         {
2026             return false;
2027         }
2028 
2029         new_type = item->type & ~cJSON_StringIsConst;
2030     }
2031 
2032     if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
2033     {
2034         hooks->deallocate(item->string);
2035     }
2036 
2037     item->string = new_key;
2038     item->type = new_type;
2039 
2040     return add_item_to_array(object, item);
2041 }
2042 
2043 CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
2044 {
2045     return add_item_to_object(object, string, item, &global_hooks, false);
2046 }
2047 
2048 /* Add an item to an object with constant string as key */
2049 CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
2050 {
2051     return add_item_to_object(object, string, item, &global_hooks, true);
2052 }
2053 
2054 CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
2055 {
2056     if (array == NULL)
2057     {
2058         return false;
2059     }
2060 
2061     return add_item_to_array(array, create_reference(item, &global_hooks));
2062 }
2063 
2064 CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
2065 {
2066     if ((object == NULL) || (string == NULL))
2067     {
2068         return false;
2069     }
2070 
2071     return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
2072 }
2073 
2074 CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name)
2075 {
2076     cJSON *null = cJSON_CreateNull();
2077     if (add_item_to_object(object, name, null, &global_hooks, false))
2078     {
2079         return null;
2080     }
2081 
2082     cJSON_Delete(null);
2083     return NULL;
2084 }
2085 
2086 CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name)
2087 {
2088     cJSON *true_item = cJSON_CreateTrue();
2089     if (add_item_to_object(object, name, true_item, &global_hooks, false))
2090     {
2091         return true_item;
2092     }
2093 
2094     cJSON_Delete(true_item);
2095     return NULL;
2096 }
2097 
2098 CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name)
2099 {
2100     cJSON *false_item = cJSON_CreateFalse();
2101     if (add_item_to_object(object, name, false_item, &global_hooks, false))
2102     {
2103         return false_item;
2104     }
2105 
2106     cJSON_Delete(false_item);
2107     return NULL;
2108 }
2109 
2110 CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean)
2111 {
2112     cJSON *bool_item = cJSON_CreateBool(boolean);
2113     if (add_item_to_object(object, name, bool_item, &global_hooks, false))
2114     {
2115         return bool_item;
2116     }
2117 
2118     cJSON_Delete(bool_item);
2119     return NULL;
2120 }
2121 
2122 CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number)
2123 {
2124     cJSON *number_item = cJSON_CreateNumber(number);
2125     if (add_item_to_object(object, name, number_item, &global_hooks, false))
2126     {
2127         return number_item;
2128     }
2129 
2130     cJSON_Delete(number_item);
2131     return NULL;
2132 }
2133 
2134 CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string)
2135 {
2136     cJSON *string_item = cJSON_CreateString(string);
2137     if (add_item_to_object(object, name, string_item, &global_hooks, false))
2138     {
2139         return string_item;
2140     }
2141 
2142     cJSON_Delete(string_item);
2143     return NULL;
2144 }
2145 
2146 CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw)
2147 {
2148     cJSON *raw_item = cJSON_CreateRaw(raw);
2149     if (add_item_to_object(object, name, raw_item, &global_hooks, false))
2150     {
2151         return raw_item;
2152     }
2153 
2154     cJSON_Delete(raw_item);
2155     return NULL;
2156 }
2157 
2158 CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name)
2159 {
2160     cJSON *object_item = cJSON_CreateObject();
2161     if (add_item_to_object(object, name, object_item, &global_hooks, false))
2162     {
2163         return object_item;
2164     }
2165 
2166     cJSON_Delete(object_item);
2167     return NULL;
2168 }
2169 
2170 CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name)
2171 {
2172     cJSON *array = cJSON_CreateArray();
2173     if (add_item_to_object(object, name, array, &global_hooks, false))
2174     {
2175         return array;
2176     }
2177 
2178     cJSON_Delete(array);
2179     return NULL;
2180 }
2181 
2182 CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
2183 {
2184     if ((parent == NULL) || (item == NULL))
2185     {
2186         return NULL;
2187     }
2188 
2189     if (item != parent->child)
2190     {
2191         /* not the first element */
2192         item->prev->next = item->next;
2193     }
2194     if (item->next != NULL)
2195     {
2196         /* not the last element */
2197         item->next->prev = item->prev;
2198     }
2199 
2200     if (item == parent->child)
2201     {
2202         /* first element */
2203         parent->child = item->next;
2204     }
2205     /* make sure the detached item doesn't point anywhere anymore */
2206     item->prev = NULL;
2207     item->next = NULL;
2208 
2209     return item;
2210 }
2211 
2212 CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
2213 {
2214     if (which < 0)
2215     {
2216         return NULL;
2217     }
2218 
2219     return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
2220 }
2221 
2222 CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
2223 {
2224     cJSON_Delete(cJSON_DetachItemFromArray(array, which));
2225 }
2226 
2227 CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
2228 {
2229     cJSON *to_detach = cJSON_GetObjectItem(object, string);
2230 
2231     return cJSON_DetachItemViaPointer(object, to_detach);
2232 }
2233 
2234 CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
2235 {
2236     cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
2237 
2238     return cJSON_DetachItemViaPointer(object, to_detach);
2239 }
2240 
2241 CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
2242 {
2243     cJSON_Delete(cJSON_DetachItemFromObject(object, string));
2244 }
2245 
2246 CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
2247 {
2248     cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
2249 }
2250 
2251 /* Replace array/object items with new ones. */
2252 CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
2253 {
2254     cJSON *after_inserted = NULL;
2255 
2256     if (which < 0)
2257     {
2258         return false;
2259     }
2260 
2261     after_inserted = get_array_item(array, (size_t)which);
2262     if (after_inserted == NULL)
2263     {
2264         return add_item_to_array(array, newitem);
2265     }
2266 
2267     newitem->next = after_inserted;
2268     newitem->prev = after_inserted->prev;
2269     after_inserted->prev = newitem;
2270     if (after_inserted == array->child)
2271     {
2272         array->child = newitem;
2273     }
2274     else
2275     {
2276         newitem->prev->next = newitem;
2277     }
2278     return true;
2279 }
2280 
2281 CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
2282 {
2283     if ((parent == NULL) || (replacement == NULL) || (item == NULL))
2284     {
2285         return false;
2286     }
2287 
2288     if (replacement == item)
2289     {
2290         return true;
2291     }
2292 
2293     replacement->next = item->next;
2294     replacement->prev = item->prev;
2295 
2296     if (replacement->next != NULL)
2297     {
2298         replacement->next->prev = replacement;
2299     }
2300     if (parent->child == item)
2301     {
2302         parent->child = replacement;
2303     }
2304     else
2305     {   /*
2306          * To find the last item in array quickly, we use prev in array.
2307          * We can't modify the last item's next pointer where this item was the parent's child
2308          */
2309         if (replacement->prev != NULL)
2310         {
2311             replacement->prev->next = replacement;
2312         }
2313     }
2314 
2315     item->next = NULL;
2316     item->prev = NULL;
2317     cJSON_Delete(item);
2318 
2319     return true;
2320 }
2321 
2322 CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
2323 {
2324     if (which < 0)
2325     {
2326         return false;
2327     }
2328 
2329     return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
2330 }
2331 
2332 static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
2333 {
2334     if ((replacement == NULL) || (string == NULL))
2335     {
2336         return false;
2337     }
2338 
2339     /* replace the name in the replacement */
2340     if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
2341     {
2342         cJSON_free(replacement->string);
2343     }
2344     replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
2345     replacement->type &= ~cJSON_StringIsConst;
2346 
2347     return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
2348 }
2349 
2350 CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
2351 {
2352     return replace_item_in_object(object, string, newitem, false);
2353 }
2354 
2355 CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
2356 {
2357     return replace_item_in_object(object, string, newitem, true);
2358 }
2359 
2360 /* Create basic types: */
2361 CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
2362 {
2363     cJSON *item = cJSON_New_Item(&global_hooks);
2364     if(item)
2365     {
2366         item->type = cJSON_NULL;
2367     }
2368 
2369     return item;
2370 }
2371 
2372 CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
2373 {
2374     cJSON *item = cJSON_New_Item(&global_hooks);
2375     if(item)
2376     {
2377         item->type = cJSON_True;
2378     }
2379 
2380     return item;
2381 }
2382 
2383 CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
2384 {
2385     cJSON *item = cJSON_New_Item(&global_hooks);
2386     if(item)
2387     {
2388         item->type = cJSON_False;
2389     }
2390 
2391     return item;
2392 }
2393 
2394 CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
2395 {
2396     cJSON *item = cJSON_New_Item(&global_hooks);
2397     if(item)
2398     {
2399         item->type = boolean ? cJSON_True : cJSON_False;
2400     }
2401 
2402     return item;
2403 }
2404 
2405 CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
2406 {
2407     cJSON *item = cJSON_New_Item(&global_hooks);
2408     if(item)
2409     {
2410         item->type = cJSON_Number;
2411         item->valuedouble = num;
2412 
2413         /* use saturation in case of overflow */
2414         if (num >= INT_MAX)
2415         {
2416             item->valueint = INT_MAX;
2417         }
2418         else if (num <= (double)INT_MIN)
2419         {
2420             item->valueint = INT_MIN;
2421         }
2422         else
2423         {
2424             item->valueint = (int)num;
2425         }
2426     }
2427 
2428     return item;
2429 }
2430 
2431 CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
2432 {
2433     cJSON *item = cJSON_New_Item(&global_hooks);
2434     if(item)
2435     {
2436         item->type = cJSON_String;
2437         item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
2438         if(!item->valuestring)
2439         {
2440             cJSON_Delete(item);
2441             return NULL;
2442         }
2443     }
2444 
2445     return item;
2446 }
2447 
2448 CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
2449 {
2450     cJSON *item = cJSON_New_Item(&global_hooks);
2451     if (item != NULL)
2452     {
2453         item->type = cJSON_String | cJSON_IsReference;
2454         item->valuestring = (char*)cast_away_const(string);
2455     }
2456 
2457     return item;
2458 }
2459 
2460 CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
2461 {
2462     cJSON *item = cJSON_New_Item(&global_hooks);
2463     if (item != NULL) {
2464         item->type = cJSON_Object | cJSON_IsReference;
2465         item->child = (cJSON*)cast_away_const(child);
2466     }
2467 
2468     return item;
2469 }
2470 
2471 CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) {
2472     cJSON *item = cJSON_New_Item(&global_hooks);
2473     if (item != NULL) {
2474         item->type = cJSON_Array | cJSON_IsReference;
2475         item->child = (cJSON*)cast_away_const(child);
2476     }
2477 
2478     return item;
2479 }
2480 
2481 CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
2482 {
2483     cJSON *item = cJSON_New_Item(&global_hooks);
2484     if(item)
2485     {
2486         item->type = cJSON_Raw;
2487         item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
2488         if(!item->valuestring)
2489         {
2490             cJSON_Delete(item);
2491             return NULL;
2492         }
2493     }
2494 
2495     return item;
2496 }
2497 
2498 CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
2499 {
2500     cJSON *item = cJSON_New_Item(&global_hooks);
2501     if(item)
2502     {
2503         item->type=cJSON_Array;
2504     }
2505 
2506     return item;
2507 }
2508 
2509 CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
2510 {
2511     cJSON *item = cJSON_New_Item(&global_hooks);
2512     if (item)
2513     {
2514         item->type = cJSON_Object;
2515     }
2516 
2517     return item;
2518 }
2519 
2520 /* Create Arrays: */
2521 CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
2522 {
2523     size_t i = 0;
2524     cJSON *n = NULL;
2525     cJSON *p = NULL;
2526     cJSON *a = NULL;
2527 
2528     if ((count < 0) || (numbers == NULL))
2529     {
2530         return NULL;
2531     }
2532 
2533     a = cJSON_CreateArray();
2534     for(i = 0; a && (i < (size_t)count); i++)
2535     {
2536         n = cJSON_CreateNumber(numbers[i]);
2537         if (!n)
2538         {
2539             cJSON_Delete(a);
2540             return NULL;
2541         }
2542         if(!i)
2543         {
2544             a->child = n;
2545         }
2546         else
2547         {
2548             suffix_object(p, n);
2549         }
2550         p = n;
2551     }
2552 
2553     return a;
2554 }
2555 
2556 CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
2557 {
2558     size_t i = 0;
2559     cJSON *n = NULL;
2560     cJSON *p = NULL;
2561     cJSON *a = NULL;
2562 
2563     if ((count < 0) || (numbers == NULL))
2564     {
2565         return NULL;
2566     }
2567 
2568     a = cJSON_CreateArray();
2569 
2570     for(i = 0; a && (i < (size_t)count); i++)
2571     {
2572         n = cJSON_CreateNumber((double)numbers[i]);
2573         if(!n)
2574         {
2575             cJSON_Delete(a);
2576             return NULL;
2577         }
2578         if(!i)
2579         {
2580             a->child = n;
2581         }
2582         else
2583         {
2584             suffix_object(p, n);
2585         }
2586         p = n;
2587     }
2588 
2589     return a;
2590 }
2591 
2592 CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
2593 {
2594     size_t i = 0;
2595     cJSON *n = NULL;
2596     cJSON *p = NULL;
2597     cJSON *a = NULL;
2598 
2599     if ((count < 0) || (numbers == NULL))
2600     {
2601         return NULL;
2602     }
2603 
2604     a = cJSON_CreateArray();
2605 
2606     for(i = 0;a && (i < (size_t)count); i++)
2607     {
2608         n = cJSON_CreateNumber(numbers[i]);
2609         if(!n)
2610         {
2611             cJSON_Delete(a);
2612             return NULL;
2613         }
2614         if(!i)
2615         {
2616             a->child = n;
2617         }
2618         else
2619         {
2620             suffix_object(p, n);
2621         }
2622         p = n;
2623     }
2624 
2625     return a;
2626 }
2627 
2628 CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count)
2629 {
2630     size_t i = 0;
2631     cJSON *n = NULL;
2632     cJSON *p = NULL;
2633     cJSON *a = NULL;
2634 
2635     if ((count < 0) || (strings == NULL))
2636     {
2637         return NULL;
2638     }
2639 
2640     a = cJSON_CreateArray();
2641 
2642     for (i = 0; a && (i < (size_t)count); i++)
2643     {
2644         n = cJSON_CreateString(strings[i]);
2645         if(!n)
2646         {
2647             cJSON_Delete(a);
2648             return NULL;
2649         }
2650         if(!i)
2651         {
2652             a->child = n;
2653         }
2654         else
2655         {
2656             suffix_object(p,n);
2657         }
2658         p = n;
2659     }
2660 
2661     return a;
2662 }
2663 
2664 /* Duplication */
2665 CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
2666 {
2667     cJSON *newitem = NULL;
2668     cJSON *child = NULL;
2669     cJSON *next = NULL;
2670     cJSON *newchild = NULL;
2671 
2672     /* Bail on bad ptr */
2673     if (!item)
2674     {
2675         goto fail;
2676     }
2677     /* Create new item */
2678     newitem = cJSON_New_Item(&global_hooks);
2679     if (!newitem)
2680     {
2681         goto fail;
2682     }
2683     /* Copy over all vars */
2684     newitem->type = item->type & (~cJSON_IsReference);
2685     newitem->valueint = item->valueint;
2686     newitem->valuedouble = item->valuedouble;
2687     if (item->valuestring)
2688     {
2689         newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
2690         if (!newitem->valuestring)
2691         {
2692             goto fail;
2693         }
2694     }
2695     if (item->string)
2696     {
2697         newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
2698         if (!newitem->string)
2699         {
2700             goto fail;
2701         }
2702     }
2703     /* If non-recursive, then we're done! */
2704     if (!recurse)
2705     {
2706         return newitem;
2707     }
2708     /* Walk the ->next chain for the child. */
2709     child = item->child;
2710     while (child != NULL)
2711     {
2712         newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */
2713         if (!newchild)
2714         {
2715             goto fail;
2716         }
2717         if (next != NULL)
2718         {
2719             /* If newitem->child already set, then crosswire ->prev and ->next and move on */
2720             next->next = newchild;
2721             newchild->prev = next;
2722             next = newchild;
2723         }
2724         else
2725         {
2726             /* Set newitem->child and move to it */
2727             newitem->child = newchild;
2728             next = newchild;
2729         }
2730         child = child->next;
2731     }
2732 
2733     return newitem;
2734 
2735 fail:
2736     if (newitem != NULL)
2737     {
2738         cJSON_Delete(newitem);
2739     }
2740 
2741     return NULL;
2742 }
2743 
2744 static void skip_oneline_comment(char **input)
2745 {
2746     *input += static_strlen("//");
2747 
2748     for (; (*input)[0] != '\0'; ++(*input))
2749     {
2750         if ((*input)[0] == '\n') {
2751             *input += static_strlen("\n");
2752             return;
2753         }
2754     }
2755 }
2756 
2757 static void skip_multiline_comment(char **input)
2758 {
2759     *input += static_strlen("/*");
2760 
2761     for (; (*input)[0] != '\0'; ++(*input))
2762     {
2763         if (((*input)[0] == '*') && ((*input)[1] == '/'))
2764         {
2765             *input += static_strlen("*/");
2766             return;
2767         }
2768     }
2769 }
2770 
2771 static void minify_string(char **input, char **output) {
2772     (*output)[0] = (*input)[0];
2773     *input += static_strlen("\"");
2774     *output += static_strlen("\"");
2775 
2776 
2777     for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
2778         (*output)[0] = (*input)[0];
2779 
2780         if ((*input)[0] == '\"') {
2781             (*output)[0] = '\"';
2782             *input += static_strlen("\"");
2783             *output += static_strlen("\"");
2784             return;
2785         } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
2786             (*output)[1] = (*input)[1];
2787             *input += static_strlen("\"");
2788             *output += static_strlen("\"");
2789         }
2790     }
2791 }
2792 
2793 CJSON_PUBLIC(void) cJSON_Minify(char *json)
2794 {
2795     char *into = json;
2796 
2797     if (json == NULL)
2798     {
2799         return;
2800     }
2801 
2802     while (json[0] != '\0')
2803     {
2804         switch (json[0])
2805         {
2806             case ' ':
2807             case '\t':
2808             case '\r':
2809             case '\n':
2810                 json++;
2811                 break;
2812 
2813             case '/':
2814                 if (json[1] == '/')
2815                 {
2816                     skip_oneline_comment(&json);
2817                 }
2818                 else if (json[1] == '*')
2819                 {
2820                     skip_multiline_comment(&json);
2821                 } else {
2822                     json++;
2823                 }
2824                 break;
2825 
2826             case '\"':
2827                 minify_string(&json, (char**)&into);
2828                 break;
2829 
2830             default:
2831                 into[0] = json[0];
2832                 json++;
2833                 into++;
2834         }
2835     }
2836 
2837     /* and null-terminate. */
2838     *into = '\0';
2839 }
2840 
2841 CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
2842 {
2843     if (item == NULL)
2844     {
2845         return false;
2846     }
2847 
2848     return (item->type & 0xFF) == cJSON_Invalid;
2849 }
2850 
2851 CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item)
2852 {
2853     if (item == NULL)
2854     {
2855         return false;
2856     }
2857 
2858     return (item->type & 0xFF) == cJSON_False;
2859 }
2860 
2861 CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item)
2862 {
2863     if (item == NULL)
2864     {
2865         return false;
2866     }
2867 
2868     return (item->type & 0xff) == cJSON_True;
2869 }
2870 
2871 
2872 CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item)
2873 {
2874     if (item == NULL)
2875     {
2876         return false;
2877     }
2878 
2879     return (item->type & (cJSON_True | cJSON_False)) != 0;
2880 }
2881 CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item)
2882 {
2883     if (item == NULL)
2884     {
2885         return false;
2886     }
2887 
2888     return (item->type & 0xFF) == cJSON_NULL;
2889 }
2890 
2891 CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
2892 {
2893     if (item == NULL)
2894     {
2895         return false;
2896     }
2897 
2898     return (item->type & 0xFF) == cJSON_Number;
2899 }
2900 
2901 CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
2902 {
2903     if (item == NULL)
2904     {
2905         return false;
2906     }
2907 
2908     return (item->type & 0xFF) == cJSON_String;
2909 }
2910 
2911 CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item)
2912 {
2913     if (item == NULL)
2914     {
2915         return false;
2916     }
2917 
2918     return (item->type & 0xFF) == cJSON_Array;
2919 }
2920 
2921 CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item)
2922 {
2923     if (item == NULL)
2924     {
2925         return false;
2926     }
2927 
2928     return (item->type & 0xFF) == cJSON_Object;
2929 }
2930 
2931 CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
2932 {
2933     if (item == NULL)
2934     {
2935         return false;
2936     }
2937 
2938     return (item->type & 0xFF) == cJSON_Raw;
2939 }
2940 
2941 CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
2942 {
2943     if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a))
2944     {
2945         return false;
2946     }
2947 
2948     /* check if type is valid */
2949     switch (a->type & 0xFF)
2950     {
2951         case cJSON_False:
2952         case cJSON_True:
2953         case cJSON_NULL:
2954         case cJSON_Number:
2955         case cJSON_String:
2956         case cJSON_Raw:
2957         case cJSON_Array:
2958         case cJSON_Object:
2959             break;
2960 
2961         default:
2962             return false;
2963     }
2964 
2965     /* identical objects are equal */
2966     if (a == b)
2967     {
2968         return true;
2969     }
2970 
2971     switch (a->type & 0xFF)
2972     {
2973         /* in these cases and equal type is enough */
2974         case cJSON_False:
2975         case cJSON_True:
2976         case cJSON_NULL:
2977             return true;
2978 
2979         case cJSON_Number:
2980             if (compare_double(a->valuedouble, b->valuedouble))
2981             {
2982                 return true;
2983             }
2984             return false;
2985 
2986         case cJSON_String:
2987         case cJSON_Raw:
2988             if ((a->valuestring == NULL) || (b->valuestring == NULL))
2989             {
2990                 return false;
2991             }
2992             if (strcmp(a->valuestring, b->valuestring) == 0)
2993             {
2994                 return true;
2995             }
2996 
2997             return false;
2998 
2999         case cJSON_Array:
3000         {
3001             cJSON *a_element = a->child;
3002             cJSON *b_element = b->child;
3003 
3004             for (; (a_element != NULL) && (b_element != NULL);)
3005             {
3006                 if (!cJSON_Compare(a_element, b_element, case_sensitive))
3007                 {
3008                     return false;
3009                 }
3010 
3011                 a_element = a_element->next;
3012                 b_element = b_element->next;
3013             }
3014 
3015             /* one of the arrays is longer than the other */
3016             if (a_element != b_element) {
3017                 return false;
3018             }
3019 
3020             return true;
3021         }
3022 
3023         case cJSON_Object:
3024         {
3025             cJSON *a_element = NULL;
3026             cJSON *b_element = NULL;
3027             cJSON_ArrayForEach(a_element, a)
3028             {
3029                 /* TODO This has O(n^2) runtime, which is horrible! */
3030                 b_element = get_object_item(b, a_element->string, case_sensitive);
3031                 if (b_element == NULL)
3032                 {
3033                     return false;
3034                 }
3035 
3036                 if (!cJSON_Compare(a_element, b_element, case_sensitive))
3037                 {
3038                     return false;
3039                 }
3040             }
3041 
3042             /* doing this twice, once on a and b to prevent true comparison if a subset of b
3043              * TODO: Do this the proper way, this is just a fix for now */
3044             cJSON_ArrayForEach(b_element, b)
3045             {
3046                 a_element = get_object_item(a, b_element->string, case_sensitive);
3047                 if (a_element == NULL)
3048                 {
3049                     return false;
3050                 }
3051 
3052                 if (!cJSON_Compare(b_element, a_element, case_sensitive))
3053                 {
3054                     return false;
3055                 }
3056             }
3057 
3058             return true;
3059         }
3060 
3061         default:
3062             return false;
3063     }
3064 }
3065 
3066 CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
3067 {
3068     return global_hooks.allocate(size);
3069 }
3070 
3071 CJSON_PUBLIC(void) cJSON_free(void *object)
3072 {
3073     global_hooks.deallocate(object);
3074 }

View Code

 

 參考:

https://blog.csdn.net/shizhe0123/article/details/94742514

https://blog.csdn.net/fengxinlinux/article/details/53121287

https://www.cnblogs.com/skullboyer/p/8152157.html

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

※別再煩惱如何寫文案,掌握八大原則!

循序漸進VUE+Element 前端應用開發(8)— 樹列表組件的使用,循序漸進VUE+Element 前端應用開發(5)— 表格列表頁面的查詢,列表展示和字段轉義處理,

在我前面隨筆《循序漸進VUE+Element 前端應用開發(6)— 常規Element 界面組件的使用》裏面曾經介紹過一些常規的界面組件的處理,主要介紹到單文本輸入框、多文本框、下拉列表,以及按鈕、圖片展示、彈出對話框、表單處理,本篇隨筆補充這一個主題,介紹樹列表組件和下拉列表樹組件在項目中的使用,以及一個SplitPanel的組件。

1、常規樹列表控件的使用

眾所周知,一般界面很多情況涉及到樹列表的處理,如類型展示,如果是一層的,可以用下拉列表代替,如果是多個層級的,採用樹控件展示會更加直觀。

在Element裏面也有一個el-tree的控件,如下所示,這裏主要對它的各種屬性和方法進行介紹。

 簡單的代碼如下所示

<el-tree :data="data"  @node-click="handleNodeClick"></el-tree>

主要在script部分裏面指定它的data數據,以及單擊節點的事件處理,結合卡片控件的展示,我們可以把樹放在其中進行展示

 界面代碼如下所示,通過 default-expand-all 可以設置全部展開,icon-class 指定節點圖標(也可以默認不指定)

        <el-card class="box-card">
          <div slot="header" class="clearfix">
            <span>樹列表</span>
            <el-button style="float: right; padding: 3px 0" type="text">操作按鈕</el-button>
          </div>
          <div>
            <el-tree
              style="padding-top:10px"
              :data="treedata"
              node-key="id"
              default-expand-all
              icon-class="el-icon-price-tag"
              highlight-current
              @node-click="handleNodeClick"
            >
              <span slot-scope="{ node, data }" class="custom-tree-node">
                <span>
                  <i :class="node.icon ? node.icon : 'el-icon-price-tag'" />
                  {{ node.label }}
               &nbsp;&nbsp;
                </span>
              </span>
            </el-tree>
          </div>
        </el-card>

其中界面裏面,我們通過 class=”custom-tree-node”,來指定樹列表的展現內容,可以加入圖標等信息

而在script裏面,定義了一個treedata的屬性

      // 初始化樹列表
      treedata: [
        {
          label: '一級 1',
          id: '1',
          children: [{
            id: '1-1',
            label: '二級 1-1',
            children: [{
              label: '三級 1-1-1',
              id: '1-1-1'
            }, {
              label: '三級 1-1-2',
              id: '1-1-2'
            }, {
              label: '三級 1-1-3',
              id: '1-1-3'
            }]
          }]
        }
      ]

如果設置有選擇框,得到界面如下所示。

 主要設置  show-checkbox 和 @check-change=”handleCheckChange” 即可。

界面代碼如下所示

<el-tree
  style="padding-top:10px"
  :data="treedata"
  node-key="id"
  default-expand-all
  highlight-current
  show-checkbox
  :default-checked-keys="['1-1-1']"
  @node-click="handleNodeClick" @check-change="handleCheckChange"
>
  <span slot-scope="{ node, data }" class="custom-tree-node">
    <span>
      <i :class="node.icon ? node.icon : 'el-icon-price-tag'" />
      {{ node.label }}
   &nbsp;&nbsp;
    </span>
  </span>
</el-tree>

而對於樹列表,可以進行一個過濾處理操作,如下界面所示。

 在內容區增加一個input的文本框進行過濾處理,並綁定對應的屬性變量

<el-input
  v-model="filterText"
  placeholder="輸入關鍵字進行過濾"
  clearable
  prefix-icon="el-icon-search"
/>

樹列表控件需要增加過濾函數綁定 :filter-node-method=”filterNode”,如下代碼所示。

<el-tree
  ref="tree"
  class="filter-tree"
  style="padding-top:10px"
  :data="treedata"
  node-key="id"
  default-expand-all
  highlight-current
  show-checkbox
  :filter-node-method="filterNode"
  @check-change="handleCheckChange"
  @node-click="handleNodeClick"
>
  <span slot-scope="{ node, data }" class="custom-tree-node">
    <span>
      <i :class="node.icon ? node.icon : 'el-icon-price-tag'" />
      {{ node.label }}
   &nbsp;&nbsp;
    </span>
  </span>
</el-tree>

script的處理代碼如下所示,需要watch過濾的綁定值,變化就進行過濾處理。

 為了在列表結合中進行快速的過濾,我們可以在上次介紹的列表界面裏面增加一個樹列表的快速查詢處理。如下界面所示。

 這裏列表裡面增加了一個第三方組件 splitpanes,用來劃分區塊展示,而且可以拖動,非常不錯,地址是:https://github.com/antoniandre/splitpanes

這個組件的Demo展示地址如下所示:https://antoniandre.github.io/splitpanes

效果大概如下所示

  npm 安裝如下所示

npm i --S splitpanes

安裝成功后,然後在vue文件的script部分裏面引入即可

import { Splitpanes, Pane } from 'splitpanes'
import 'splitpanes/dist/splitpanes.css'

它的使用代碼也很簡單

<splitpanes style="height: 400px">
  <pane min-size="20">1</pane>
  <pane>
    <splitpanes horizontal>
      <pane>2</pane>
      <pane>3</pane>
      <pane>4<pane>
    </splitpanes>
  </pane>
  <pane>5</pane>
</splitpanes>

我的列表界面使用了兩個Panel即可實現左側樹的展示,和右側常規列表查詢的處理。

  

2、下拉框樹列表的處理

除了常規的樹列表展示內容外,我們也需要一個在下拉列表中展示樹內容的界面組件。

這裏又得引入一個第三方的界面組件,因此Element的Select組件不支持樹列表。

GitHub地址:https://github.com/riophae/vue-treeselect

官網地址:https://vue-treeselect.js.org/

NPM安裝:

npm install --save @riophae/vue-treeselect

界面代碼如下所示。

<template>
  <div id="app">
    <treeselect v-model="value" :multiple="true" :options="options" />
  </div>
</template>

這裏的value就是選中的集合,options則是樹列表的節點數據。

<script>
  // import the component
  import Treeselect from '@riophae/vue-treeselect'
  // import the styles
  import '@riophae/vue-treeselect/dist/vue-treeselect.css'

  export default {
    // register the component
    components: { Treeselect },
    data() {
      return {
        // define the default value
        value: null,
        // define options
        options: [ {
          id: 'a',
          label: 'a',
          children: [ {
            id: 'aa',
            label: 'aa',
          }, {
            id: 'ab',
            label: 'ab',
          } ],
        }, {
          id: 'b',
          label: 'b',
        }, {
          id: 'c',
          label: 'c',
        } ],
      }
    },
  }
</script>

我的測試界面代碼如下所示

          <div style="height:180px">
            <!--
                v-model 綁定選中的集合
                options 樹節點數據
                 defaultExpandLevel 展開層次,Infinity為所有
                 flat 為子節點不影響父節點,不關聯
             -->
            <treeselect
              v-model="value"
              :options="treedata"
              :multiple="true"
              :flat="true"
              :default-expand-level="Infinity"
              :open-on-click="true"
              :open-on-focus="true"
              clearable
              :max-height="200"
            />
          </div>
<script>
// import vue-treeselect component
import Treeselect from '@riophae/vue-treeselect'
// import the styles
import '@riophae/vue-treeselect/dist/vue-treeselect.css'

export default {
  name: 'Tree',
  components: { Treeselect },
  data() {
    return {
      // 過濾條件
      filterText: '',
      // 初始化樹列表
      treedata: [
        {
          label: '一級 1',
          id: '1',
          children: [{
            id: '1-1',
            label: '二級 1-1',
            children: [{
              label: '三級 1-1-1',
              id: '1-1-1'
            }, {
              label: '三級 1-1-2',
              id: '1-1-2'
            }, {
              label: '三級 1-1-3',
              id: '1-1-3'
            }]
          }]
        }
      ],
      value: ['1-1-2']
    }
  },
................
}
</script>

來一張幾個樹列表一起的對比展示界面。

 以上就是普通樹列表和下拉列表樹展示的界面效果,往往我們一些特殊的界面處理,就需要多利用一些封裝良好的第三方界面組件實現,可以豐富我們的界面展示效果。

 

列出以下前面幾篇隨筆的連接,供參考:

循序漸進VUE+Element 前端應用開發(1)— 開發環境的準備工作

循序漸進VUE+Element 前端應用開發(2)— Vuex中的API、Store和View的使用

循序漸進VUE+Element 前端應用開發(3)— 動態菜單和路由的關聯處理

循序漸進VUE+Element 前端應用開發(4)— 獲取後端數據及產品信息頁面的處理

循序漸進VUE+Element 前端應用開發(5)— 表格列表頁面的查詢,列表展示和字段轉義處理

循序漸進VUE+Element 前端應用開發(6)— 常規Element 界面組件的使用

循序漸進VUE+Element 前端應用開發(7)— 介紹一些常規的JS處理函數

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!