傳聞 2021 年款全新 MacBook Pro 或將把 HDMI 接口、 SD 讀卡機回歸

不知不覺 Apple 在 MacBook Pro 加入 Touch Bar 觸控列已經也有近五年時間,不過上個月才傳出 2021 年 MacBook Pro 可能會取消 Touch Bar 並回歸實體鍵盤,當時就有傳聞指出有可能 MagSafe 磁吸充電和更多 I/O 介面也可能回歸。近日,根據天風證券分析師郭明錤(Ming-Chi Kuo)的報告預測,今年下半年登場的 2021 年款全新 MacBook Pro  幾有多個設計和規格的變化,將配備 SD 卡插槽與 HDMI 接口。

▲圖片來源:MacRumors

傳聞 2021 年款全新 MacBook Pro 或將把 HDMI 接口、 SD 讀卡機回歸

根據天風證券分析師郭明錤(Ming-Chi Kuo)的預測, Apple 預計在今年下半年推出全新設計的 14 吋與 16 吋兩款 MacBook Pro ,在設計上將有許多對使用者實用的改變,包括之前曾提到將採用 mini-LED 顯示器、回歸經典的 MagSafe 磁吸充電、取消 Touch Bar 觸控列。另外,近日也提到新款的 MacBook Pro 將恢復在機身上配置 SD 記憶卡插槽和 HDMI 接口,這對於經常有外接螢幕、記憶卡傳輸需求的用戶來說,未來也將不必再另外攜帶 USB Hub 轉接器。
郭明錤也預測,新款 MacBook Pro 將配備 SD 卡插槽與 HDMI 接口,而此實用改變將有助於推升換機需求。

▲圖片來源:Apple Hub (Twitter/ @theapplehub)

然而,對於目前已經逐漸習慣以 USB-C 轉接其他像是 SD 記憶卡、 HDMI、 USB-A 、 RJ45 等介面的 MacBook Pro 使用者來說,只在有需要時才將 USB-C Hub 轉接器接上使用,平時也能減少接口未使用入塵的機會,也不完全是件壞事。另外,考量到機身設計最實際要面對的就是機身厚度限制,若回歸這些介面勢必會讓機身厚度再次增加。那麼,各位是否真的會希望 Apple 在新版 MacBook Pro 加回 HDMI 接口與 SD 記憶卡插槽呢?

消息來源:MacRumors

延伸閱讀:
小米推出 33W GaN 氮化鎵充電器:超小體積、 iPhone 也能快速充電,售價僅約 340 元

LINE 24 款免費貼圖整理:多款 LINE 貼圖免費下載!

您也許會喜歡:

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

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

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

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

※超省錢租車方案

※回頭車貨運收費標準

新一代華為 MateBook 14 輕薄筆電在台推出,美感、效能一步到位

華為旗下的筆電產品以及精緻、便利與高 CP 在台灣有一定的愛用者,今日(2/24)更宣布最新 MateBook 14 在台灣推出,延續過去兼顧精工雕琢的外表與效能平衡兩大方面,讓生產力與娛樂齊頭並進,為消費者開啟全新的 2K 輕薄螢幕新視野,多工處理體驗一次到位。

新一代華為 MateBook 14 輕薄筆電在台推出,美感、效能一步到位

MateBook 14 擁有鋁合金搭配 CNC 鑽切工藝,加上精密噴砂與鑽石倒腳切割,打造出手感細膩的高質感輕薄金屬機身,重量僅 1.49kg 恰到好處,低調簡約卻有耐看百搭。

搭配 3:2 顯示比例的 2K 超高解析度全螢幕,螢幕佔比達到 90%,並且覆蓋 100% sRGB 寬廣色域,通過德國萊茵低藍光護眼測試,視野清晰一覽無遺。

搭載 7nm 製程的 AMD Ryzen 4000H 系列行動處理器,與 AMD Radeon 系列顯示晶片,16GB 雙通道記憶體搭配 512GB PCIe NVMe SSD,小小的身軀裡面卻蘊含能涵蓋足以支援生產力、視訊短片與娛樂遊戲各方面的高效能。改良的全新鯊魚鰭熱系統,將葉片厚度減少 40%,葉片數量提升 49%,以增加 39% 風量、低噪音的雙風扇、雙導管來維持整體系統穩定性。

在鍵盤右上方則是電源搭配指紋辨識器:這款新機同樣地將視訊鏡頭隱藏於鍵盤之中,不用時就收起來,確保個人隱私無虞。華為筆電的另一個招牌功能「一碰傳」在這款同樣基因的產品上當然也沒有省略,加大觸控板碰傳區域,隨手傳超便利。內建 56Wh 大容量電池,配合華為自家的智慧省電技術,只需充電 15 分鐘就能使用 2.5 小時,隨附的 65W 口袋充電器重量輕巧僅 160g,隨時補充電量不給你負擔。

華為 MateBook 14 售價定為 30,990 元,上市期間購機就送 Office 365 個人版與羅技 M235 無線滑鼠,數量有限送完為止。另外,即日起至 2/28 止,只要在燦坤指定店點(請見下圖)、線上賣場與華為品牌店完成預購,就送市價 6,988 元的 ViewSonic 27 吋 2K 薄型顯示器一部。

整體算下來這台搭載 AMD 4600H處理器、16GB RAM、512GB SSD、全金屬機身、長效續航的筆電只賣30990已經相當高性價比了,如果扣掉價值近7000元的27吋螢幕、OFFICE365與滑鼠,等於這樣規格的筆電不到23000!相當划算,有興趣的朋友可以到華為品牌店庾燦坤門市購買(請點我):

您也許會喜歡:

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

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

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

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

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

※回頭車貨運收費標準

小米10T Lite 將於 2/26 在台發表:搭載 Snapdragon 750G 5G 處理器、120Hz 更新率螢幕、64MP 四鏡頭主相機

除了本週的 2021 小米元宵節活動有同步開賣 Redmi 9T 入門手機,今(24)日稍早小米台灣也正式宣佈即將在 26 日 12:30 正式在台推出「小米10T Lite」中階新機。小米10T Lite 搭載 120Hz 更新率變速螢幕、高通 Snapdragon 750G 5G 處理器、 6400 萬像素四鏡頭主相機、4820mAh大電池和 33W 快速充電,也將對於重視 CP 值的消費者再添一款平價 5G 可選擇!

▲圖片來源:小米香港官網

小米10T Lite 將於 2/26 在台發表:搭載 Snapdragon 750G 5G 處理器、120Hz 更新率螢幕、64MP 四鏡頭主相機

去年原本小米都未在台灣推出手機,不過在去年底至今短短兩個個月陸續推出包括小米10 Lite 、小米10T、小米10T Pro、Redmi 9T、Redmi Note 9T 等新機,今(24)日稍早也宣佈將於 26 日直播活動在台推出「小米10T Lite」5G 中階新機。由於小米10T Lite 其實已經率先在香港推出,在台灣正式發表前也先帶各位「預習」一下小米10T Lite 的規格重點。

▲圖片來源:小米台灣 Xiaomi Taiwan (Facebook)

螢幕方面,小米10T Lite 搭載 6.67 吋 20:9 顯示比例、 FHD+ 解析度的居中挖孔全螢幕,機身前後採用康寧第五代大猩猩玻璃保護:

▲圖片來源:小米香港官網

顯示方面,小米10T Lite 除了支持 1500:1 對比度、 NTSC 84% 色域、 120Hz 螢幕更新率、 240Hz 觸控採樣率,螢幕也通過德國萊茵低藍光認證。另外,小米10T Lite 前後也配備智慧環境光感應器,能提供最舒適的視覺體驗。螢幕採用陽光螢幕 3.0 ,在陽光直射下自動增強對比度以確保畫面清晰可見:

▲圖片來源:小米香港官網

小米10T Lite 搭載 6400 萬像素四鏡頭主相機,採用 6400 萬像素 F/1.89 光圈主鏡頭(SONY IMX682 感光元件)、800 萬像素 120° 超廣角鏡頭、200 萬像素 4cm 微距鏡頭以及 200 萬像素景深鏡頭的搭配。

▲圖片來源:小米香港官網

前相機則配備 1600 萬像素前置自拍鏡頭:

▲圖片來源:小米香港官網

小米10T Lite 相機應用功能也相當豐富,像是也支持前後鏡頭同時錄影:

▲圖片來源:小米香港官網

硬體規格方面,小米10T Lite 搭載 Qualcomm Snapdragon 750G 5G 處理器,香港版本推出 6GB RAM 和 128GB ROM 單一規格,台灣版本確切規格則得等待小米台灣官方正式發佈為準。

▲圖片來源:小米香港官網

小米10T Lite 內建 4820mAh 大電量電池,支持 33W 快速充電能在 59 分鐘充電至 100% :
▲圖片來源:小米香港官網

▲圖片來源:小米香港官網

其他方面,小米10T Lite 支援雙頻 GPS、多功能 NFC、1216 線性揚聲器,生物辨識則採用結合電源鍵的側面指紋解鎖方案。

▲圖片來源:小米香港官網

小米10T Lite 也將於 2 月 26 日 12:30 的「小米5G手機超值選」直播正式登場,當日觀看直播還有機會抽小米手機(點我前往):

▲圖片來源:小米台灣官網

消息來源:小米台灣 Xiaomi Taiwan (Facebook)|小米台灣官網

延伸閱讀:
小米推出 33W GaN 氮化鎵充電器:超小體積、 iPhone 也能快速充電,售價僅約 340 元

小米米家筋膜槍眾籌推出:強勁動力有效放鬆筋膜,眾籌價約 1,942 元

您也許會喜歡:

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

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

【其他文章推薦】

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

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

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

※南投搬家公司費用需注意的眉眉角角,別等搬了再說!

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

※回頭車貨運收費標準

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

Anker 率先為 iPhone 12 系列推出「磁吸無線充電行動電源」,外出充電更方便

前段時間我們曾報導過 iOS 14.5 Beta 2 暗示將推出 MagSafe 充電配件的消息,近期 Anker 這間致力於研發充電配件品牌也針對 iPhone 12 系列推出磁吸式無線充電行動電源「Anker PowerCore Magnetic 5K Wireless Power Bank」。凡是適用支援 MagSafe 的 iPhone 12 系列手機,就能直接將行動電源吸附在機身背面直接進行無線充電。

Anker 率先為 iPhone 12 系列推出「磁吸無線充電行動電源」,外出充電更方便

在 CES 2021 首次亮相的 Anker PowerCore 系列磁吸無線充電行動電源,能通過磁力吸附固定在 iPhone 12 系列背蓋上進行無線充電。近日這款行動電源也搶先 Apple 原廠率先推出、悄悄在 Amazon 上架。
有別於 Apple 原廠的聰穎電池護殼或第三方配件品牌的充電保護殼,這類磁吸無線充電行動電源將能讓使用者在未充電狀態能將行動電源收在包包,只在需要充電期間直接吸附上去即可進行充電。

Anker 這款行動電源內建 5,000mAh 的電池容量,可為 iPhone 12 mini 從 0% 充電至 100% 、 iPhone 12 和 iPhone 12 Pro 從 0% 充電至 95% 、 iPhone 12 Pro Max 從 0% 充電至 75% 。雖然行動電源電池容量不大、僅能作為電力補給使用,但對於不想攜帶充電線、作為一整天下來最後緊急補給電力使用,這款行動電源還是能滿足需求。除了能透過無線充電進行充電,這款行動電源也支持 USB-C 雙向輸出充電:

行動電源支持最高 5W 無線充電、磁性也夠強,能牢牢吸附在 iPhone 12 系列機身背蓋邊充電邊使用:

除了裸機能充電,這款行動電源也可搭配 MagSafe 保護殼的 iPhone 12 系列進行磁吸無線充電:

充電安全方面,也支持溫度保護、過充保護和異物檢測等安全機制:

售價方面, Anker PowerCore 磁西無線充電行動電源 5000mAh 的售價為 39.99 美元(約合新台幣 1,117 元),目前已在 Amazon 上架。

圖片/消息來源:Amazon

延伸閱讀:
小米推出 33W GaN 氮化鎵充電器:超小體積、 iPhone 也能快速充電,售價僅約 340 元

傳聞 2021 年款全新 MacBook Pro 或將把 HDMI 接口、 SD 讀卡機回歸

您也許會喜歡:

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

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

【其他文章推薦】

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

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

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

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

※台中搬家公司費用怎麼算?

Go文件操作

目錄

  • 示例1: 打開和關閉文件
  • 示例2: 打開文件並讀取內容
  • 示例3: 一次性讀取文件
  • 示例4: 帶緩衝的Reader讀文件
  • 示例5: 創建文件並寫入內容
  • 示例6: 寫文件的四種方式
  • 示例7: 把一個文件內容寫入到另一個文件
  • 示例8:使用bufio獲取用戶輸入
  • 示例9: 判斷文件或目錄是否存在
  • 示例10: 拷貝文件、圖片音視頻
  • 示例11: 遍歷目錄
    • 遍歷目錄
    • 僅遍歷目錄,忽略文件
  • 示例12: 修改文件名
  • 示例13:創建目錄
  • 示例14:刪除文件

對於文件,我們並不陌生,文件是數據源(保存數據的地方)的一種,比如大家經常使用的word文檔,txt文件,Excel文件…等等都是文件。文件最主要的作用就是保存數據,它既可以保存一張圖片,也可以保存視頻,聲音……
文件在程序中是以流的形式來操作的。

流:數據在數據源(文件)和程序(內存)之間經歷的路徑
輸出流:數據從程序(內存)到數據源(文件)的路徑
輸入流:數據從數據源(文件)到程序(內存)的路徑
輸入與輸出都是相對於內存而言的,從內存向外流就是輸出,從外部向內存流就是輸入

在Go中,我們操作文件的方法在os包中,會經常使用到os.File結構體 Go語言標準庫文檔

示例1: 打開和關閉文件

package main

import (
    "fmt"
    "os"
)

func main() {

    //打開文件(/Users/xxx/Go/src/file.txt)
    //概念說明:file的叫法
    //1.file 叫 file對象
    //2.file 叫 file指針
    //3.file 叫 file文件句柄
    file, err := os.Open("/Users/itbsl/Go/src/file.txt")
    if err != nil {
        fmt.Println("文件打開失敗,原因是:", err)
        //os.Exit(0)
    }
    defer func() {
        //文件及時關閉
        err = file.Close()
        if err != nil {
            fmt.Println("文件關閉失敗,原因是", err)
        }
    }()
}

示例2: 打開文件並讀取內容

使用Read()函數按照字節讀

package main

import (
	"fmt"
	"io"
	"os"
)

func main() {

	file, err := os.Open("./test.txt")
	if err != nil {
		fmt.Printf("open file failed, err:%v\n", err)
		return
	}
	defer func() {
		err = file.Close()
		if err != nil {
			fmt.Printf("close file failed, err:%v\n", err)
		}
	}()

	var content []byte
	var tmp = make([]byte, 128)
	for {
		n, err := file.Read(tmp)
		//為什麼是tmp[:n]而不是tmp[:]?
		//因為當讀取到最後一行的內容長度不足tmp的長度的時候
		//新讀取的內容只會覆蓋前半部分上次讀取到的tmp的內容,
		//後半部分還是上一次讀取的內容,如果用tmp[:]就會導致
		//後半部分久內容又會被重新賦值一次,這其實是錯誤的
		content = append(content, tmp[:n]...)
		if err == io.EOF {//讀到文件末尾
			break
		}
	}
	fmt.Printf("讀取出來的內容為:\n")
	fmt.Printf("%q\n", string(content))
}

示例3: 一次性讀取文件

讀取文件內容並显示在終端,將文件內容一次性讀取到終端,適用於文件不大的情況。

package main

import (
    "fmt"
    "io/ioutil"
)

func main() {

    //打開文件,文件路徑相對於GOPATH開始,或者寫全路徑(/Users/xxx/Go/src/file.txt)
    file, err := ioutil.ReadFile("src/file.txt")
    if err != nil {
        fmt.Println("文件打開失敗,原因是:", err)
    }

    fmt.Printf("%s", string(file))
}

示例4: 帶緩衝的Reader讀文件

讀取文件的內容並显示在終端(帶緩衝區的方式),使用os.Open, file.Close,bufio.NewReader,reader.ReadString函數和方法。適合讀取大文件
1.使用ReadBytes方法
代碼1:

package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
)

func main() {

	file, err := os.Open("./test.txt")
	if err != nil {
		log.Fatalf("open file failed, err: %v\n", err)
	}
	defer func() {
		err = file.Close()
		if err != nil {
			log.Fatalf("close file failed, err: %v\n", err)
		}
	}()

	//定義變量result用來存儲讀取結果
	var result string
	//創建一個帶有緩衝區的reader
	reader := bufio.NewReader(file)
	for {
		buf, err := reader.ReadBytes('\n')
		if err != nil && err == io.EOF { //EOF代表文件的末尾
			//注意:為什麼要判斷err是否等於io.EOF?
			//因為存在這種情況,文件有內容的最後那一行尾部沒有換行
			//當使用ReadBytes或者ReadString方法按照'\n'換行讀取時,讀到尾部沒有換行的這種情況時就會報io.EOF錯誤
			//此時buf是讀取到了內容的,如果忽略掉了,那麼最終的讀取結果會少了最後一行的內容
			result += string(buf)
			break
		}
		result += string(buf)
	}
	fmt.Println(result)
}

代碼2:

package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
)

func main() {

	file, err := os.Open("./test.txt")
	if err != nil {
		log.Fatalf("open file failed, err: %v\n", err)
	}
	defer func() {
		err = file.Close()
		if err != nil {
			log.Fatalf("close file failed, err: %v\n", err)
		}
	}()

	//定義變量result用來存儲讀取結果
	var result string
	//創建一個帶有緩衝區的reader
	reader := bufio.NewReader(file)
	for {
		buf, err := reader.ReadBytes('\n')
		if err != nil {
			if err == io.EOF { //EOF代表文件的末尾
			//注意:為什麼要判斷err是否等於io.EOF?
			//因為存在這種情況,文件有內容的最後那一行尾部沒有換行
			//當使用ReadBytes或者ReadString方法按照'\n'換行讀取時,讀到尾部沒有換行的這種情況時就會報io.EOF錯誤
			//此時buf是讀取到了內容的,如果忽略掉了,那麼最終的讀取結果會少了最後一行的內容
				result += string(buf)
				break
			} else {
				log.Fatalf("ReadBytes failed, err: %v\n", err)
			}
		}
		result += string(buf)
	}
	fmt.Println(result)
}

2.ReadString方法

package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
)

func main() {

    //打開文件
    file, err := os.Open("./files/test.txt")
    if err != nil {
        fmt.Println("文件打開失敗,原因是:", err)
        return
    }

    //當函數退出時,要及時的關閉file
    defer func() {
        //文件及時關閉
        err = file.Close()
        if err != nil {
            fmt.Println("文件關閉失敗,原因是", err)
        }
    }()

    //創建一個 *Reader,是帶緩衝的
    reader := bufio.NewReader(file)
    var result string
    //循環讀取文件內容
    for {
        str, err := reader.ReadString('\n') //讀到一個換行就結束
        result += str
        if err == io.EOF {//io.EOF代表文件的末尾
            //注意:如果文件最後一行文字沒有換行,則會一直讀取到文件末尾,
            //所以即使是判斷讀到了文件末尾,也要把讀取的內容輸出一下
            break
        }
    }
    fmt.Println(result)
}

示例5: 創建文件並寫入內容

第二個參數:文件代開模式(可以組合);第三個參數:權限控制(如0755)

package main

import (
	"fmt"
	"os"
)

func main() {

	//1.創建文件file.txt
	file, err := os.OpenFile("src/file.txt", os.O_WRONLY | os.O_CREATE, 0755)
	if err != nil {
		fmt.Println("文件打開/創建失敗,原因是:", err)
		return
	}

	defer func() {
		err  = file.Close()
		if err != nil {
			fmt.Println("文件關閉失敗,原因是:", err)
		}
	}()

	//寫入數據
	var str = "暗黑西遊獅駝嶺,斗戰勝佛孫悟空。\n"

	for i := 0; i < 5; i++ {
		file.WriteString(str)
	}
}

示例6: 寫文件的四種方式

1.使用WriteAt()搭配Seek()方法實現寫文件功能

package main

import (
	"io"
	"log"
	"os"
)

func main() {

	file, err := os.OpenFile("./test.txt", os.O_RDWR|os.O_CREATE, 0755)
	if err != nil {
		log.Fatalf("open file failed, err: %v\n", err)
	}
	defer func() {
		err = file.Close()
		if err != nil {
			log.Fatalf("close file failed, err: %v\n", err)
		}
	}()
    //Seek(): 修改文件的讀寫指針位置.
    //參數1: 偏移量. 正:向文件尾部偏移, 負:向文件頭部偏移
    //參數2: 偏移起始位置
    //       io.SeekStart: 文件起始位置
    //       io.SeekCurrent: 文件當前位置
    //       io.SeekEnd: 文件結尾位置
    //返回值:表示從文件起始位置,到當前文件讀寫指針位置的偏移量。
    //WriteAt(): 在文件指定偏移位置,寫入[]byte,通常搭配Seek()
    //參數1: 待寫入的數據
    //參數2: 偏移量
    //返回: 實際寫出的字節數
	for i := 0; i < 5; i++ {
		offset, _ := file.Seek(-3, io.SeekEnd)
		_, _ = file.WriteAt([]byte("你好"), offset)
	}
}

注意: 由於使用的OpenFile函數打開的文件,所以在選擇打開模式的時候不能選擇os.O_APPEND模式,因為該模式表示的是在文件末尾追加,這與WriteAt在指定的位置寫是想衝突的,雖然我在測試的時候加上os.O_APPEND模式並沒有報錯,但是代碼執行完之後發現,想要寫入的內容並沒有真正的寫入到文件中。
寫入前

寫入后

2.一次性寫文件

package main

import (
	"io/ioutil"
	"log"
)

func main() {
	str := "hello樹先生"
	//如果文件已存在,則會清空原來的內容,寫入新內容,如果文件不存在,則會創建文件並寫入內容
	err := ioutil.WriteFile("./test.txt", []byte(str), 0755)
	if err != nil {
		log.Fatalf("寫入文件錯誤,錯誤為:%v\n", err)
	}
}

3.使用帶緩衝的方式寫文件

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {

	//1.創建文件file.txt
	file, err := os.OpenFile("src/file.txt", os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0755)
	defer func() {
		err  = file.Close()
		if err != nil {
			fmt.Println("文件關閉失敗,原因是:", err)
		}
	}()

	if err != nil {
		fmt.Println("文件創建失敗,原因是:", err)
		return
	}

	//寫入數據
	var str = "你好,世界\n"

	//寫入時,使用帶緩存的*Writer
	writer := bufio.NewWriter(file)

	for i := 0; i < 5; i++ {
		writer.WriteString(str)
	}

	//因為writer是帶緩存的,因此在調用writeString方法時,其實內容是先寫入到緩存
	//因此需要調用Flush方法,將緩存數據寫入到文件中,否則文件中會丟失數據
	writer.Flush()
}

示例7: 把一個文件內容寫入到另一個文件

package main

import (
    "fmt"
    "io/ioutil"
)

func main() {

    //打開文件,文件路徑相對於GOPATH開始,或者寫全路徑(/Users/xxx/Go/src/file.txt)
    data, err := ioutil.ReadFile("src/1.txt")
    if err != nil {
        fmt.Println("文件打開失敗,原因是:", err)
    }

    err = ioutil.WriteFile("src/2.txt", data, 0755)

    if err != nil {
        fmt.Println("文件寫入失敗,原因是:", err)
    }
}

示例8:使用bufio獲取用戶輸入

package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    var s string
    var reader = bufio.NewReader(os.Stdin)
    s, _ = reader.ReadString('\n')
    fmt.Printf("讀取到的內容為:%s\n", s)
}

示例9: 判斷文件或目錄是否存在

Go判斷文件或文件夾是否存在的方法為使用os.Stat()函數返回的錯誤值進行判斷:
(1)如果返回的錯誤為nil,說明文件或文件夾存在
(2)如果返回的類型使用os.IsNotExist()判斷為true,說明文件或文件夾不存在
(3)如果返回的錯誤為其它類型,則不確定是否存在

package main

import (
	"fmt"
	"os"
)

func main() {

	isExist, err := isFileExists("src/sfile.txt")
	if err != nil {
		fmt.Println("發生錯誤:", err)
	}

	if isExist {
		fmt.Println("存在")
	} else {
		fmt.Println("不存在")
	}
}

//判斷文件或者目錄是否存在
func isFileExists(path string) (bool, error) {

	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

示例10: 拷貝文件、圖片音視頻

io.Copy方法

package main

import (
	"fmt"
	"io"
	"os"
)
func CopyFile(srcFileName string, dstFileName string) (int64, error) {

	//源文件處理
	srcFile, err := os.Open(srcFileName)
	defer func() {
		err = srcFile.Close()
		if err != nil {
			fmt.Println("源文件關閉失敗,原因是:", err)
		}
	}()

	if err != nil {
		fmt.Println("源文件打開失敗,原因是:", err)
		return 0, err
	}

	//目標文件處理
	dstFile, err := os.OpenFile(dstFileName, os.O_CREATE | os.O_WRONLY, 0755)
	defer func() {
		err = dstFile.Close()
		if err != nil {
			fmt.Println("目標文件關閉失敗,原因是:", err)
		}
	}()
	if err != nil {
		fmt.Println("目標文件打開失敗,原因是:", err)
		return 0, err
	}

	return io.Copy(dstFile, srcFile)
}

func main() {

	result, err := CopyFile("src/dst.jpeg", "src/哈哈.jpeg")

	if err == nil {
		fmt.Println("拷貝成功!拷貝的字節數為: ", result)
	}
}

對於大文件,我們還可以採用下面的方式

package main

import (
	"io"
	"log"
	"os"
)

func CopyFile(srcFileName string, dstFileName string) {
	//打開源文件
	srcFile, err := os.Open(srcFileName)
	if err != nil {
		log.Fatalf("源文件讀取失敗,原因是:%v\n", err)
	}
	defer func() {
		err = srcFile.Close()
		if err != nil {
			log.Fatalf("源文件關閉失敗,原因是:%v\n", err)
		}
	}()

	//創建目標文件,稍後會向這個目標文件寫入拷貝內容
	distFile, err := os.Create(dstFileName)
	if err != nil {
		log.Fatalf("目標文件創建失敗,原因是:%v\n", err)
	}
	defer func() {
		err = distFile.Close()
		if err != nil {
			log.Fatalf("目標文件關閉失敗,原因是:%v\n", err)
		}
	}()
	//定義指定長度的字節切片,每次最多讀取指定長度
	var tmp = make([]byte, 1024*4)
	//循環讀取並寫入
	for {
		n, err := srcFile.Read(tmp)
		n, _ = distFile.Write(tmp[:n])
		if err != nil {
			if err == io.EOF {//讀到了文件末尾,並且寫入完畢,任務完成返回(關閉文件的操作由defer來完成)
				return
			} else {
				log.Fatalf("拷貝過程中發生錯誤,錯誤原因為:%v\n", err)
			}
		}
	}
}

func main() {
	CopyFile("./worm.mp4", "./dist.mp4")
}

示例11: 遍歷目錄

遍歷目錄

package main

//我們讀寫的文件一般存放於目錄中.因此,有時需要指定到某一個目錄下,根據目錄存儲的狀況
//再進行文件的特定操作.接下來我們看看目錄的基本操作方法.
import (
	"fmt"
	"log"
	"os"
)
//打開目錄
//打開目錄我們也使用OpenFile函數,但要指定不同的參數來通知系統,要打開的是一個目錄文件.
//func OpenFile(name string, flag int, perm FileMode) (file *File, err error)
//參數1: name,表示要打開的目錄名稱.使用絕對路徑較多
//參數2: flag,表示打開文件的讀寫模式
//參數3: perm,表示打開權限.但對於目錄來說有所不同,通常傳os.ModeDir.
//返回值:由於是操作目錄,所以file是指向目錄的文件指針.err中保存錯誤信息

//讀目錄內容
//這與讀文件有所不同.目錄中存放的是文件名和子目錄名.所以使用Readdir函數
//func (f *File) Readdir(n int) (fi []FileInfo, err error)
//如果n>0,Readdir函數會返回一個最多n個成員的切片。這時,如果Readdir返回一個空切片,
//它會返回一個非nil的錯誤說明原因。如果到達了目錄f的結尾,返回值err會是io.EOF。
//
//如果n<=0,Readdir函數返回目錄中剩餘所有文件對象的FileInfo構成的切片。
//此時,如果Readdir調用成功(讀取所有內容直到結尾),它會返回該切片和nil的錯誤值。
//如果在到達結尾前遇到錯誤,會返回之前成功讀取的FileInfo構成的切片和該錯誤。

func main() {
	//不推薦,因為通過查看ioutil.ReadDir()函數可知,官方使用的是os.Open()函數打開的目錄
	//file, err := os.OpenFile("./dir", os.O_RDWR, os.ModeDir)
	file, err := os.Open("./dir")
	if err != nil {
		log.Fatalf("文件打開失敗,原因是:%v\n", err)
	}
	defer func() {
		err = file.Close()
		if err != nil {
			log.Fatalf("文件關閉失敗,原因是:%v\n", err)
		}
	}()
	//Readdir方法返回一個FileInfo接口類型的切片和一個error類型的錯誤
	infos, err := file.Readdir(-1)
	for _, info := range infos {
		fmt.Printf("%v, %v\n", info.Name(), info.IsDir())
	}
}

僅遍歷目錄,忽略文件

方法1:使用os包

package main

import (
    "fmt"
    "os"
)

var dirNames = make([]string, 0, 50)
var pathSeparator = string(os.PathSeparator)
func traverseDir(filePath string) error {
    file, err := os.Open(filePath)
    if err != nil {
        return err
    }
    fileInfo, err := file.Readdir(0)
    if err != nil {
        return err
    }

    for _, value := range fileInfo {
        if value.IsDir() {
            dirNames = append(dirNames, value.Name())
            err = traverseDir(filePath+pathSeparator+value.Name())
            if err != nil {
                return err
            }
        }
    }
    return err
}

func main() {

    var filePath = "./dir"
    err := traverseDir(filePath)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(dirNames)
}

方法2:使用ioutil包

package main

import (
    "fmt"
    "io/ioutil"
    "os"
)

var dirNames = make([]string, 0, 50)
var pathSeparator = string(os.PathSeparator)
func traverseDir(filePath string) error {
    fileInfos, err := ioutil.ReadDir(filePath)
    if err != nil {
        return err
    }
    for _, fileInfo :=range fileInfos {
        if fileInfo.IsDir() {
            dirNames = append(dirNames, fileInfo.Name())
            err =  traverseDir(filePath+pathSeparator+fileInfo.Name())
            if err != nil {
                return err
            }
        }
    }
    return err
}

func main() {

    var filePath = "./dir"
    err := traverseDir(filePath)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(dirNames)
}

示例12: 修改文件名

package main

import (
    "fmt"
    "io/ioutil"
    "os"
    "strings"
)

var pathSeparator = string(os.PathSeparator)
//重命名文件
func renameFileName(filePath string, old string, new string) error {
    files, err := ioutil.ReadDir(filePath)
    if err != nil {
        return err
    }
    for _, fileInfo := range files {
        if !fileInfo.IsDir() {
            err = os.Rename(filePath + pathSeparator + fileInfo.Name(),
                filePath + pathSeparator + strings.Replace(fileInfo.Name(), old, new, -1),
            )
            if err != nil {
                return err
            }
        }
    }
    return err
}

func main() {
    var filePath = "./dir"
    err := renameFileName(filePath, "f", "kkk")
    if err != nil {
        fmt.Printf("錯誤: %v\n", err)
    }
}

示例13:創建目錄

package main

import (
	"fmt"
	"os"
)

func main() {
	//Mkdir使用指定的權限和名稱創建一個目錄。如果出錯,會返回*PathError底層類型的錯誤。
	err := os.Mkdir("./foo", 0755)
	if os.IsExist(err) {
		fmt.Println("目錄已存在")
		return
	}

	//MkdirAll使用指定的權限和名稱創建一個目錄,包括任何必要的上級目錄,並返回nil,否則返回錯誤。
	//權限位perm會應用在每一個被本函數創建的目錄上。如果path指定了一個已經存在的目錄,MkdirAll不做任何操作並返回nil。
	err = os.MkdirAll("./foo/bar", 0755)
	if err != nil {
		fmt.Printf("%v\n", err)
		return
	}
}

示例14:刪除文件

package main

import (
	"fmt"
	"os"
)

func main() {
	//Remove刪除name指定的文件或目錄。如果出錯,會返回*PathError底層類型的錯誤。
	//該方法不能刪除非空目錄,如果想刪除目錄以及目錄下的所有文件,可以使用RemoveAll
	err := os.Remove("./def")
	if os.IsNotExist(err) {
		fmt.Println("您要刪除的文件或目錄不存在")
		return
	}
	if err != nil {
		fmt.Println(err)
	}

	//RemoveAll刪除path指定的文件,或目錄及它包含的任何下級對象。
	//它會嘗試刪除所有東西,除非遇到錯誤並返回。
	//如果path指定的對象不存在,RemoveAll會返回nil而不返回錯誤。
	err = os.RemoveAll("./def")
	if err != nil {
		fmt.Println(err)
	}
}

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

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

※台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

※台中搬家公司費用怎麼算?

【原創】Linux中斷子系統(二)-通用框架處理

背景

  • Read the fucking source code! –By 魯迅
  • A picture is worth a thousand words. –By 高爾基

說明:

  1. Kernel版本:4.14
  2. ARM64處理器,Contex-A53,雙核
  3. 使用工具:Source Insight 3.5, Visio

1. 概述

【原創】Linux中斷子系統(一)-中斷控制器及驅動分析講到了底層硬件GIC驅動,以及Arch-Specific的中斷代碼,本文將研究下通用的中斷處理的過程,屬於硬件無關層。當然,我還是建議你看一下上篇文章。

這篇文章會解答兩個問題:

  1. 用戶是怎麼使用中斷的(中斷註冊)?
  2. 外設觸發中斷信號時,最終是怎麼調用到中斷handler的(中斷處理)?

2. 數據結構分析

先來看一下總的數據結構,核心是圍繞着struct irq_desc來展開:

  • Linux內核的中斷處理,圍繞着中斷描述符結構struct irq_desc展開,內核提供了兩種中斷描述符組織形式:

    1. 打開CONFIG_SPARSE_IRQ宏(中斷編號不連續),中斷描述符以radix-tree來組織,用戶在初始化時進行動態分配,然後再插入radix-tree中;
    2. 關閉CONFIG_SPARSE_IRQ宏(中斷編號連續),中斷描述符以數組的形式組織,並且已經分配好;
    3. 不管哪種形式,最終都可以通過linux irq號來找到對應的中斷描述符;
  • 圖的左側灰色部分,主要在中斷控制器驅動中進行初始化設置,包括各個結構中函數指針的指向等,其中struct irq_chip用於對中斷控制器的硬件操作,struct irq_domain與中斷控制器對應,完成的工作是硬件中斷號到Linux irq的映射;

  • 圖的上側灰色部分,中斷描述符的創建(這裏指CONFIG_SPARSE_IRQ),主要在獲取設備中斷信息的過程中完成的,從而讓設備樹中的中斷能與具體的中斷描述符irq_desc匹配;

  • 圖中剩餘部分,在設備申請註冊中斷的過程中進行設置,比如struct irqaction中handler的設置,這個用於指向我們設備驅動程序中的中斷處理函數了;

中斷的處理主要有以下幾個功能模塊:

  1. 硬件中斷號到Linux irq中斷號的映射,並創建好irq_desc中斷描述符;
  2. 中斷註冊時,先獲取設備的中斷號,根據中斷號找到對應的irq_desc,並將設備的中斷處理函數添加到irq_desc中;
  3. 設備觸發中斷信號時,根據硬件中斷號得到Linux irq中斷號,找到對應的irq_desc,最終調用到設備的中斷處理函數;

上述的描述比較簡單,更詳細的過程,往下看吧。

3. 流程分析

3.1 中斷註冊

這一次,讓我們以問題的方式來展開:
先來讓我們回答第一個問題:用戶是怎麼使用中斷的?

  1. 熟悉設備驅動的同學應該都清楚,經常會在驅動程序中調用request_irq()接口或者request_threaded_irq()接口來註冊設備的中斷處理函數;
  2. request_irq()/request_threaded_irq接口中,都需要用到irq,也就是中斷號,那麼這个中斷號是從哪裡來的呢?它是Linux irq,它又是如何映射到具體的硬件設備的中斷號的呢?

先來看第二個問題:設備硬件中斷號到Linux irq中斷號的映射

  • 硬件設備的中斷信息都在設備樹device tree中進行了描述,在系統啟動過程中,這些信息都已經加載到內存中並得到了解析;
  • 驅動中通常會使用platform_get_irq或irq_of_parse_and_map接口,去根據設備樹的信息去創建映射關係(硬件中斷號到linux irq中斷號映射);
  • 【原創】Linux中斷子系統(一)-中斷控制器及驅動分析提到過struct irq_domain用於完成映射工作,因此在irq_create_fwspec_mapping接口中,會先去找到匹配的irq domain,再去回調該irq domain中的函數集,通常irq domain都是在中斷控制器驅動中初始化的,以ARM GICv2為例,最終回調到gic_irq_domain_hierarchy_ops中的函數;
  • 如果已經創建好了映射,那麼可以直接進行返回linux irq中斷號了,否則的話需要irq_domain_alloc_irqs來創建映射關係;
  • irq_domain_alloc_irqs完成兩個工作:
    1. 針對linux irq中斷號創建一個irq_desc中斷描述符;
    2. 調用domain->ops->alloc函數來完成映射,在ARM GICv2驅動中對應gic_irq_domain_alloc函數,這個函數很關鍵,所以下文介紹一下;

gic_irq_domain_alloc函數如下:

  • gic_irq_domain_translate:負責解析出設備樹中描述的中斷號和中斷觸發類型(邊緣觸發、電平觸發等);
  • gic_irq_domain_map:將硬件中斷號和linux中斷號綁定到一個結構中,也就完成了映射,此外還綁定了irq_desc結構中的其他字段,最重要的是設置了irq_desc->handle_irq的函數指針,這個最終是中斷響應時往上執行的入口,這個是關鍵,下文講述中斷處理過程時還會提到;
  • 根據硬件中斷號的範圍設置irq_desc->handle_irq的指針,共享中斷入口為handle_fasteoi_irq,私有中斷入口為handle_percpu_devid_irq;

上述函數執行完成后,完成了兩大工作:

  1. 硬件中斷號與Linux中斷號完成映射,併為Linux中斷號創建了irq_desc中斷描述符;
  2. 數據結構的綁定及初始化,關鍵的地方是設置了中斷處理往上執行的入口;

再看第一個問題:中斷是怎麼來註冊的?

設備驅動中,獲取到了irq中斷號后,通常就會採用request_irq/request_threaded_irq來註冊中斷,其中request_irq用於註冊普通處理的中斷,request_threaded_irq用於註冊線程化處理的中斷;

在講具體的註冊流程前,先看一下主要的中斷標誌位:

#define IRQF_SHARED		0x00000080              //多個設備共享一个中斷號,需要外設硬件支持
#define IRQF_PROBE_SHARED	0x00000100              //中斷處理程序允許sharing mismatch發生
#define __IRQF_TIMER		0x00000200              //時鐘中斷
#define IRQF_PERCPU		0x00000400              //屬於特定CPU的中斷
#define IRQF_NOBALANCING	0x00000800              //禁止在CPU之間進行中斷均衡處理
#define IRQF_IRQPOLL		0x00001000              //中斷被用作輪訓
#define IRQF_ONESHOT		0x00002000              //一次性觸發的中斷,不能嵌套,1)在硬件中斷處理完成后才能打開中斷;2)在中斷線程化中保持關閉狀態,直到該中斷源上的所有thread_fn函數都執行完
#define IRQF_NO_SUSPEND		0x00004000              //系統休眠喚醒操作中,不關閉該中斷
#define IRQF_FORCE_RESUME	0x00008000              //系統喚醒過程中必須強制打開該中斷
#define IRQF_NO_THREAD		0x00010000              //禁止中斷線程化
#define IRQF_EARLY_RESUME	0x00020000              //系統喚醒過程中在syscore階段resume,而不用等到設備resume階段
#define IRQF_COND_SUSPEND	0x00040000              //與NO_SUSPEND的用戶共享中斷時,執行本設備的中斷處理函數

  • request_irq也是調用request_threaded_irq,只是在傳參的時候,線程處理函數thread_fn函數設置成NULL;
  • 由於在硬件中斷號和Linux中斷號完成映射后,irq_desc已經創建好,可以通過irq_to_desc接口去獲取對應的irq_desc;
  • 創建irqaction,並初始化該結構體中的各個字段,其中包括傳入的中斷處理函數賦值給對應的字段;
  • __setup_irq用於完成中斷的相關設置,包括中斷線程化的處理:
    1. 中斷線程化用於減少系統關中斷的時間,增強系統的實時性;
    2. ARM64默認開啟了CONFIG_IRQ_FORCED_THREADING,引導參數傳入threadirqs時,則除了IRQF_NO_THREAD外的中斷,其他的都將強制線程化處理;
    3. 中斷線程化會為每个中斷都創建一個內核線程,如果中斷進行共享,對應irqaction將連接成鏈表,每個irqaction都有thread_mask位圖字段,當所有共享中斷都處理完成后才能unmask中斷,解除中斷屏蔽;

3.2 中斷處理

當完成中斷的註冊后,所有結構的組織關係都已經建立好,剩下的工作就是當信號來臨時,進行中斷的處理工作。

來回顧一下【原創】Linux中斷子系統(一)-中斷控制器及驅動分析中的Arch-specific處理流程:

  • 中斷收到之後,首先會跳轉到異常向量表的入口處,進而逐級進行回調處理,最終調用到generic_handle_irq來進行中斷處理。

generic_handle_irq處理如下圖:

  • generic_handle_irq函數最終會調用到desc->handle_irq(),這個也就是對應到上文中在建立映射關係的過程中,調用irq_domain_set_info函數,設置好了函數指針,也就是handle_fasteoi_irq和handle_percpu_devid_irq;
  • handle_fasteoi_irq:處理共享中斷,並且遍歷irqaction鏈表,逐個調用action->handler()函數,這個函數正是設備驅動程序調用request_irq/request_threaded_irq接口註冊的中斷處理函數,此外如果中斷線程化處理的話,還會調用__irq_wake_thread()喚醒內核線程;
  • handle_percpu_devid_irq:處理per-CPU中斷處理,在這個過程中會分別調用中斷控制器的處理函數進行硬件操作,該函數調用action->handler()來進行中斷處理;

來看看中斷線程化處理后的喚醒流程吧__handle_irq_event_percpu->__irq_wake_thread:

  • __handle_irq_event_percpu->__irq_wake_thread將喚醒irq_thread中斷內核線程;
  • irq_thread內核線程,將根據是否為強制中斷線程化對函數指針handler_fn進行初始化,以便後續進行調用;
  • irq_thread內核線程將while(!irq_wait_for_interrupt)循環進行中斷的處理,當滿足條件時,執行handler_fn,在該函數中最終調用action->thread_fn,也就是完成了中斷的處理;
  • irq_wait_for_interrupt函數,將會判斷中斷線程的喚醒條件,如果滿足了,則將當前任務設置成TASK_RUNNING狀態,並返回0,這樣就能執行中斷的處理,否則就調用schedule()進行調度,讓出CPU,並將任務設置成TASK_INTERRUPTIBLE可中斷睡眠狀態;

3.3 總結

中斷的處理,總體來說可以分為兩部分來看:

  1. 從上到下:圍繞irq_desc中斷描述符建立好連接關係,這個過程就包括:中斷源信息的解析(設備樹),硬件中斷號到Linux中斷號的映射關係、irq_desc結構的分配及初始化(內部各個結構的組織關係)、中斷的註冊(填充irq_desc結構,包括handler處理函數)等,總而言之,就是完成靜態關係創建,為中斷處理做好準備;
  2. 從下到上,當外設觸發中斷信號時,中斷控制器接收到信號併發送到處理器,此時處理器進行異常模式切換,並逐步從處理器架構相關代碼逐級回調。如果涉及到中斷線程化,則還需要進行中斷內核線程的喚醒操作,最終完成中斷處理函數的執行。

歡迎關注個人公眾號,不定期分享Linux內核機制文章

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

【其他文章推薦】

※產品缺大量曝光嗎?你需要的是一流包裝設計!

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

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※超省錢租車方案

※台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

※推薦台中搬家公司優質服務,可到府估價

為.netcore助力–WebApiClient正式發布core版本

1 前言

WebApiClient已成熟穩定,發布了WebApiClient.JIT和WebApiClient.AOT兩個nuget包,累計近10w次下載。我對它的高可擴展性設計相當滿意和自豪,但WebApiClient並不因此而停下腳步,在一年前,我產生了編寫其core版本的想法,將asp.netcore服務端先進的思想融入到core版本,在性能與擴展性上得到進一步升華。
對應的,給它叫了WebApiClientCore的名字,為了對得起名字裏面的Core字,我在框架設計、性能優化上佔用整體開發時間一半以上。

2 框架設計

IActionInvoker

WebApiClient時還沒有IActionInvoker概念,對應的執行邏輯直接在ApiActionContext上實現。現在我覺得,Context應該是一個狀態數據類,而不能也成為一個執行者,因為一個執行者的實例可以無限次地執行多個Context實例。

Refit則更簡單粗暴,將所有實現都在一個RequestBuilderImplementation的類上:你們只要也只能使用我內置的Attribute聲明,一切執行在我這個類裡面包辦,因為我是一個萬能類。

Core版本增加了IActionInvoker概念,從中Context分開,用於執行Context,職責分明。在實現上又分為多種Invoker:Task聲明返回執行者ActionInvoker、ITask聲明返回處理處理者ActionTask,以及聚合的MultiplexedActionInvoker。

Middleware思想

WebApiClient時在處理各個特性、參數驗證、返回值驗證時沒有使用Middleware思想,特別是在處理響應結果和異常短路邏輯難以維護。

Refit還是簡單粗暴,將所有特性的解釋實現都在這個RequestBuilderImplementation的類上,因為我還是一個萬能類。

Core版本增加中間件Builder,將請求前的相關Attribute的執行編排Build為一個請求處理委託,將請求后相關Attribute的執行編排Build為一個響應處理委託,然後把兩個委託與真實http請求串在一起,Build出一個完整的請求響應委託。

得益於Middleware,流程中的請求前參數值驗證、結果處理特性短路、異常短路、請求后結果值驗和無條件執行IApiFilterAtrribue等這些複雜的流程變成簡單的管道處理;另外接口也變成支持服務端響應多種格式內容,每種格式內容在一個IApiReturnAttribute上實現和處理,比如請求為Accept: application/json, application/xml,不管服務器返回xml或json都能處理。

/// <summary>
/// 創建執行委託
/// </summary>
/// <param name="apiAction">action描述器</param>
/// <returns></returns>
public static Func<ApiRequestContext, Task<ApiResponseContext>> Build(ApiActionDescriptor apiAction)
{
    var requestHandler = BuildRequestHandler(apiAction);
    var responseHandler = BuildResponseHandler(apiAction);

    return async request =>
    {
        await requestHandler(request).ConfigureAwait(false);
        var response = await HttpRequest.SendAsync(request).ConfigureAwait(false);
        await responseHandler(response).ConfigureAwait(false);
        return response;
    };
}

Context思想

WebApiClient只有一個ApiActionContext,其Result和Exception屬性在請求前就可以訪問或設置,但實際上就算設置了值,流程也不會短路和中斷,屬於設計失誤。

Refit沒有相關Context概念,因為它不提供給用戶自定義擴展Attribute的能力,它內置的Attribute也沒有執行能力,一個RequestBuilderImplementation類夠了。

Core版本將設計了多個Context概念,不同階段有不同的Context,如同asp.netcore不同Filter的Context也不同一樣。對於一個Action,請求階段對應是ApiRequestContext,響應階段是ApiResponseContext;對於Action的參數,對應是ApiParameterContext。每種Context裏面都包含核心的HttpContext屬性,HttpContext包含請求消息、響應消息和接口配置選項等。

Interface思想

輸入WebApiClientCore命名空間,會發現定義了很多Interface,這些Interface都是為了用戶實現自定義特性用的,當然內置的所有特性,都是實現了這些接口而已。如果一個特性實現了多個接口,它就有多種能力,比如內置的HeaderAttribute,它既可以修飾於Interface和Method,也可以修飾參數。

WebApiClientCore的Attribute描述的邏輯,是由Attribute自我實現,所以整個請求的數據裝配邏輯是分散為各個Attribute上,用什麼Attribute就有什麼邏輯,包含框架之外的非內置的自定義Attribute。

Refit的內置Attribute只有欲描述邏輯,沒有實現邏輯,實現邏輯由RequestBuilderImplementation包辦,所以它不需要接口也沒有接口。

3 性能優化

更快的字符串替換

像[HttpGet(“objects/{id}”)]這樣的path參數,在restful中常常遇到,通過Span優化,Core版本在替換path參數值cpu佔用時間降低為原版本的十分之一。

更快的json序列化

得益於Sytem.Text.Json,json序列化和反序列化上性能顯明提升。

更少的緩衝區分配

WebApiClientCore使用了可回收復用的IBufferWriter,在json序列化得到json、json裝配為HttpContent只申請一次Buffer,而且HttpContent在發送之後,這個Buffer被回收復用。IBufferWriter還於用表單的uri編碼,編碼產生的Buffer不用申請新的內存內容,直接寫入表單的HttpContent。

更少的編碼操作

WebApiClientCore的json不再使用utf16的string中間類型,直接將對象序列化為網絡請求需要的utf8編碼二進制json;表單的key和Value編碼時,也不產生string中間類型,而是編碼后的二進制數據內容,然後寫入表單的IBufferWriter。

更快的緩存查找

WebApiClient創建代理類實例來執行一個請求時,要查找兩次緩存:通過接口類型查找字典緩存的接口代理類,然後實例化代理類;在ApiInterceptor裏面通過MethodInfo查找字典緩存的ApiActionDescriptor。

Refit執行同樣邏輯也使用了兩次字典緩存,接口和接口代理類安全字典緩存TypeMapping,接口和接口方法描述的字典緩存interfaceHttpMethods。

WebApiClientCore取消了字典緩存,使用靜態泛型類的字段作緩存,因為字段訪問遠比字典查找高效。同時通過巧妙的設計,在代理類攔截方法執行時,直接回傳IActionInvoker替換原來的MethodInfo,IActionInvoker包含了ApiActionDescriptor,而IActionInvoker與代理類型都一起緩存在靜態泛型類的字段,減少了一次必須的字典緩存查找過程。

性能對比

排除掉真實的網絡請求IO等待時間,WebApiClientCore使用的cpu時間僅僅為WebApiClient.JIT和Refit的三分之一。

BenchmarkDotNet=v0.12.1, OS=Windows 10.0.18362.836 (1903/May2019Update/19H1)
Intel Core i3-4150 CPU 3.50GHz (Haswell), 1 CPU, 4 logical and 2 physical cores
.NET Core SDK=3.1.202
  [Host]     : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT
  DefaultJob : .NET Core 3.1.4 (CoreCLR 4.700.20.20201, CoreFX 4.700.20.22101), X64 RyuJIT
Method Mean Error StdDev
HttpClient_GetAsync 3.146 μs 0.0396 μs 0.0370 μs
WebApiClientCore_GetAsync 12.421 μs 0.2324 μs 0.2174 μs
Refit_GetAsync 43.241 μs 0.6713 μs 0.6279 μs
Method Mean Error StdDev
HttpClient_PostJsonAsync 5.263 μs 0.0784 μs 0.0733 μs
WebApiClientCore_PostJsonAsync 13.823 μs 0.1874 μs 0.1753 μs
Refit_PostJsonAsync 45.218 μs 0.8166 μs 0.7639 μs

4 Nuget包與文檔

Nuget包

<PackageReference Include="WebApiClientCore" Version="1.0.*" />

項目地址與文檔

點擊項目鏈接,帶你GET到N種使用技能,不求star,只求提供良好建議。

https://github.com/dotnetcore/WebApiClient

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

【其他文章推薦】

※回頭車貨運收費標準

※產品缺大量曝光嗎?你需要的是一流包裝設計!

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

※推薦評價好的iphone維修中心

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

※台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

※台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

不可不知的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

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

【其他文章推薦】

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

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

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

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

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

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

【JVM故事】了解JVM的結構,好在面試時吹牛


class文件格式

參考上一篇文章《【JVM故事】一個Java字節碼文件的誕生記》,後續還會專門講解class文件的內部結構。

數據類型

jvm包括兩種數據類型,基本類型和引用類型。

基本類型包括,數值類型,boolean類型,和returnAddress類型。

數值類型包括,整型,浮點型,和char類型。

boolean類型同樣只有true和false。

returnAddress類型是一個指針,指向jvm指令的操作碼,在Java中沒有與之對應的類型。

boolean類型的操作會被轉化為int類型的操作進行,boolean數組會當成byte數組去操作。1表示true,0表示false。

引用類型包括三種,類類型,數組類型,和接口類型。

它們的值是動態創建的類實例,數組,或實現接口的類實例。

數組有component類型和element類型,component類型就是數組去掉最外層維度后剩下的類型,可能還是一個數組類型(對於多維數組)。

element類型就是數組裡面存儲的最小數據的類型,它必須是一個基本類型,類類型,或接口類型。

對於一維數組的話,component類型和element類型是相同的。

引用類型還有一個特殊值,就是null,表示沒有引用任何對象。

運行時公有數據區

堆

jvm有一個堆,在所有jvm線程間共享,堆是一個運行時數據區域,所有為類實例和數組分配的內存都來自於它。

堆在jvm啟動時創建,堆中對象不用顯式釋放,gc會幫我們釋放並回收內存。

方法區

jvm有一個方法區,在所有jvm線程間共享,它存儲每一個類的結構。

像運行時常量池,字段和方法數據,方法和構造函數的代碼,還有特殊的方法用於類和實例的初始化,以及接口的初始化。

方法區在jvm啟動時創建,雖然方法區在邏輯上是堆的一部分。

但簡單實現時可以選擇不進行gc和壓縮,本規範沒有強制要求方法區的位置,也沒有要求管理已編譯代碼的策略。

運行時常量池

運行時常量池就是類或接口的字節碼文件里的常量池的運行時表示形式,它包含幾種常量。

如在編譯時就已經知道的数字字面量值,和必須在運行時解析的方法和字段的引用,運行時常量池的功能類似於傳統語言的符號表,不過它包含的數據會更加寬泛。

運行時常量池分配在jvm的方法區,類或接口的運行時常量池在類或接口被jvm創建時才會構建。

運行時私有數據區

pc寄存器

jvm支持一次運行多個線程,每個線程都有自己的pc寄存器,任何時候一個線程只能運行一個方法的代碼。

如果方法不是native的,pc寄存器包含當前正在被執行的jvm指令地址,如果方法是native的,pc寄存器的值是未定義的。

jvm棧

每一個jvm線程都有一個私有的jvm棧,隨着線程的創建而創建,棧中存儲的是幀。

jvm棧和傳統語言如C的棧相似,保存局部變量和部分計算結果,參与方法的調用和返回。jvm棧主要用於幀的出棧和入棧,除此之外沒有其它操作,

幀可能是在堆上分配的,所以jvm棧使用的內存不必是連續的。

native方法棧

native方法不是用Java語言寫的,為了支持它需要使用傳統棧,如C語言棧。不過jvm不能加載native方法,所以也不需要提供native方法需要的棧。

幀

每次當一個方法被調用時一個新的幀會被創建。當方法調用完成時,與之對應的幀會被銷毀,無論是正常完成還是拋異常結束。

所以幀是方法調用的具體體現形式,或稱方法調用是以幀的形式進行的。幀用來存儲數據和部分計算結果,和執行動態鏈接,方法返回值,分發異常。

幀分配在創建幀的線程的jvm棧上,每一個幀都有自己的本地變量數組,自己的操作數據棧,和一個對當前方法所在類的運行時常量池的引用。

本地變量數組和操作數棧的大小在編譯時就確定了,它們隨着和幀關聯的方法編譯后的代碼一起被提供,因此幀這種數據結構的大小隻依賴於jvm的實現,這些結構所需的內存可以在方法調用時同時被分配。

在一個線程執行的任何時刻,都只會有一個幀是處於激活的。這個幀被稱為當前幀,與之對應的方法被稱為當前方法,方法所在的類被稱為當前類,此時用到的本地變量數組和操作數棧也都是當前幀的。

一個幀將不在繼續是當前幀,如果它的方法調用了另一個方法,或者它的方法結束了。

當一個方法被調用,一個新的幀被創建,當執行控制由原來的方法傳遞到新的方法時,這個新的幀變為當前幀。

當方法返回時,當前幀把方法執行的結果傳回到上一幀,當上一幀被激活的同時當前幀會被丟棄。

本地變量數組

每一幀都包含一個變量數組,就是都熟知的本地變量存儲的地方。這個本地變量數組的長度在編譯時確定,隨着編譯后的方法代碼一起提供。

通常一個本地變量(的位置)能夠存儲一個類型的值,但是long和double類型卻需要兩個本地變量(的位置)才能存一個值。

本地變量按索引尋址,第一個本地變量的索引是0。long和double需要消耗兩個連續的索引,但卻是按照較小的這個索引尋址的。不能按照較大的那個索引去讀數據,但是可以寫入,當然這樣將使本地變量內容錯亂。

在方法被調用時,jvm使用本地變量來接收傳遞進來的參數值。在類(靜態)方法調用時,所有參數被傳入從索引0開始的連貫的本地變量數組裡。

在實例(非靜態)方法調用時,索引0處總是傳入正在其上執行方法調用的那個對象的引用,(就是Java中的this了),所有參數被傳入從1開始的連貫的本地變量數組裡。

操作數棧

每個幀包含一個後進先出的棧,用於存儲正在執行的jvm指令的操作數,就是都熟知的操作數棧,這個棧的最大深度在編譯時就已確定,隨着編譯后的方法代碼一起提供。

當幀被創建時,操作數棧是空的,jvm提供一些指令用於加載常量值,本地變量值,字段值到操作數棧上,另一些jvm指令採用操作數棧上的操作數進行操作,並把結果放回到操作數棧上。

操作數棧也用於準備將要傳遞給方法調用的參數和接收方法調用返回的結果。

long和double類型的值佔用兩個單位的棧深度,其它類型的值佔用一個單位的棧深度。

動態鏈接

每一個幀都包含了對當前方法所屬類型的運行時常量池的引用。目的是為了支持方法代碼的動態鏈接。class文件中描述一個方法引用被調用的方法和被訪問的變量的代碼,是採用符號引用的形式實現的。

符號引用的形式可以粗略的認為是字符串的形式,就是用字符串標明需要調用哪個類的哪個方法或訪問哪個字段或變量。就像符號引用這個名字一樣,這些僅僅是符號,是拿不到具體值的,所以必須要進行轉換。

動態鏈接就是把這些符號方法引用轉換為具體的方法引用,在必要時加載類來解析尚未明確的符號,把符號變量的訪問轉換為這些變量運行時所在存儲結構的適合的偏移量(索引)。這樣的方式又稱為後期綁定。

方法調用

一個方法調用正常完成(即沒有拋異常)時,會根據所返回的值的類型執行一個適合的return指令,當前幀會去恢復調用者的狀態,包括它的本地變量和操作數棧,使調用者的程序計數器適合的遞增來跳過剛剛的那個方法調用指令。

返回值會被放到調用者幀的操作數棧上,然後繼續執行調用者方法的幀。

一個方法在調用時拋出了異常,且這個異常沒有在這個方法內被捕獲處理,將會導致這個方法調用的突然結束,這種情況下永遠不會向方法的調用者返回一個值。

特殊方法

站在jvm的級別,每一個用Java寫的構造函數都以一個實例初始化方法出現,且都是特殊的名字,就是<init>,這個名字是編譯器提供的。

實例初始化方法只能在jvm內部使用invokespecial這個指令調用,且只能在尚未初始化的類實例上調用。

一個類或接口最多可以有一個類或接口初始化方法,通過調用這個方法被初始化。類或接口的初始化方法也有特殊的名字,就是<clinit>,該方法沒有參數,且返回值是void。

方法名稱也是由編譯器提供的,從Java7開始,在字節碼中這個方法必須被標記為靜態的才行。

這個初始化方法是被jvm隱式調用的,它們絕對不會直接被用任何jvm指令調用,僅作為類初始化進程的一部分被間接的調用。

Java類庫

jvm必須為Java類庫的實現提供足夠的支持。一些類庫中的類如果沒有jvm協助是無法實現的。

反射,就是在運行時獲取某個類的類型相關信息,如它的字段信息,方法信息,構造函數信息,父類信息,實現的接口信息。

這些信息都必須是把一個類加載完之後才可以知道的,只有jvm才可以加載類。如java.lang.reflect這個包下的類和Class這個類。

在Java中加載一個類或接口用類加載器,即ClassLoader,背後還是委託給jvm來實現的。

鏈接和初始化一個類或接口。

安全,如java.security包下的類,還有其它類像SecurityManager。

多線程,如線程這個類Thread。

弱引用,像java.lang.ref包下的類。

公有設計,私有實現

以上內容只是jvm的一個“相對寬泛”的規範,它並不是實現方案,也不是實現細節。

實現者可以根據自身的需要來實現jvm,如運行在後端服務器上的jvm和運行在移動設備上的jvm肯定側重點有所不同。

從事Java的人都知道,事實上jvm是有較多的實現版本。

由於jvm是處在Java語言和操作系統之間的,所以它要向上提供對Java的支持,向下與操作系統良好交互。

寫在最後

高級語言(Java,C#)中的很多操作如文件操作,網絡操作,內存操作,線程操作,I/O操作等,都不是高級語言自身能夠實現的。

也不是它們的虛擬機(JVM,CLR)能夠實現的,實際最終是由操作系統實現的,因為這些都是系統資源,只有操作系統才有權限訪問。

如果你用Java或C#代碼創建了一個文件,千萬不要以為是Java或C#創建了這個文件,它們只是層層向下調用了操作系統的API,然後到文件系統API,最後可能到磁盤驅動程序。

由此可以看出,要想設計一門語言,不單單是關鍵字、語法、編譯器,類庫,虛擬機這些,還要深度了解操作系統,甚至是硬件,如CPU架構和CPU指令集等。

所以,和語言相關的事情,每一項都是異常的繁瑣複雜,都需要投入大量的人力、財力、時間去研究,最後即使研究成功了,可能沒有生態,沒人使用,自然也無法賺錢。

因此,國人現在還沒有一門屬於自己的真正語言。

>>> 熱門文章集錦 <<<

 

畢業10年,我有話說

【面試】我是如何面試別人List相關知識的,深度有點長文

我是如何在畢業不久只用1年就升為開發組長的

爸爸又給Spring MVC生了個弟弟叫Spring WebFlux

【面試】我是如何在面試別人Spring事務時“套路”對方的

【面試】Spring事務面試考點吐血整理(建議珍藏)

【面試】我是如何在面試別人Redis相關知識時“軟懟”他的

【面試】吃透了這些Redis知識點,面試官一定覺得你很NB(乾貨 | 建議珍藏)

【面試】如果你這樣回答“什麼是線程安全”,面試官都會對你刮目相看(建議珍藏)

【面試】迄今為止把同步/異步/阻塞/非阻塞/BIO/NIO/AIO講的這麼清楚的好文章(快快珍藏)

【面試】一篇文章幫你徹底搞清楚“I/O多路復用”和“異步I/O”的前世今生(深度好文,建議珍藏)

【面試】如果把線程當作一個人來對待,所有問題都瞬間明白了

Java多線程通關———基礎知識挑戰

品Spring:帝國的基石

 

作者是工作超過10年的碼農,現在任架構師。喜歡研究技術,崇尚簡單快樂。追求以通俗易懂的語言解說技術,希望所有的讀者都能看懂並記住。下面是公眾號的二維碼,歡迎關注!

 

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

【其他文章推薦】

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

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

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

循序漸進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網頁設計為架站首選

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

※回頭車貨運收費標準

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

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